#include "repo.h"

#include "WLcJSON.h"
#include "md5.h"
#include "wl_ext_fs.h"

#ifdef __cplusplus
extern "C" {
#endif

// #define REPO_CREATE_TONE_HEAD

#define REPO_DNS_STORE_NAME "repo_dns.store"
#define REPO_CONFIG_STORE_NAME "repo_config.store"
#define REPO_GROUP_STORE_NAME "repo_group.store"
#define REPO_FRIEND_STORE_NAME "repo_friend.store"
#define REPO_SESSION_STORE_NAME "repo_session.store"
#define REPO_TTSINDEX_STORE_NAME "repo_ttsindex.store"
#define REPO_SYSTEM_STORE_NAME "repo_system.store"

#define MAX_REPO_STORE_SIZE 8192

#define MAX_PLAYBACK_NUM 1  // 最多5条回放

#define MAX_BUFFER_LEN 4096
#define USER_CONFIG_STRING_LEN 2048

#define MD5_SECURITY_SIZE 32

#define MAX_TTS_NUM 5  // 保存最近的TTS语音

// #define MANAGER_SESSION_KEEP  // 管理员会话保持

typedef struct __repo_system {
    wl_uint32 daemon_duration;  // 运行时长
    wl_uint32 memory_reboot;
    wl_uint32 version_num;  // 版本号
    wl_uint32 sdk_memory;   // SDK内存
    wl_uint32 memory;       // 系统内存
} repo_system;

typedef struct __repo_config {
    wl_uint32    managerid;
    wl_uint32    version;
    wl_bool      burst_session_hold;    // 当前会话保持
    wl_bool      contact_session_hold;  // 通讯录会话保持
    wl_uint32    device_type;
    wl_uint32    client_type;
    wl_uint32    active_session_lifetime;  // 活跃会话静默时间单位秒
    wl_uint32    tmp_session_keeptime;     // 临时会话保存时长
    wl_uint64    default_sessionid;        // 默认会话Id
    wl_uint16    default_sessiontype;      // 默认会话类型
    repo_account account;
    repo_corp    corp;
    repo_user    user;
    wl_uint32    location_interval;
    wl_bool      location_share;  // 位置共享
    wl_uint32    volume;
    wl_uint32    attach_limit;   // 附着群数量限制
    wl_list      geofence_list;  // 围栏
    wl_list      button_list;
    wl_char      imsi[MAX_IMSI_LEN];
    wl_char*     user_config;
} repo_config;

// 为防止文本hash碰撞,每次生成两个hash
typedef struct __repo_ttsindex {
    wl_time   timestamp;  // 按时间先后轮替
    wl_uint32 rshash;
    wl_uint32 jshash;
    wl_uint16 seq;  // tts序号,序号对应tts文件名
} repo_ttsindex;

typedef struct __repo_global {
    repo_system         system;
    repo_config         config;
    repo_appkey         appkey;
    repo_hardware       hardware;
    repo_url            login_url;
    repo_server         web_server;
    wl_char             token[MAX_TOKEN_LEN];
    wl_uint32           login_state;
    wl_uint32           extension_state;
    repo_extension_bind extension_bind;

    wl_list   dns_list;
    wl_list   group_list;
    wl_uint64 friend_updated;
    wl_list   friend_list;
    wl_list   session_list;
    wl_list   ttsindex_list;
    // 企业信息
    repo_corp corp_info;             // 企业信息
    wl_uint32 corp_group_version;    // 企业群版本
    wl_uint32 corp_address_version;  // 企业通讯录版本

    wl_uint64 attach_sessionid;     // attach session
    wl_uint64 active_sessionid;     // 活跃会话Id
    wl_uint16 active_sessiontype;   // 活跃会话类型
    wl_uint64 default_sessionid;    // 默认会话Id
    wl_uint16 default_sessiontype;  // 默认会话类型

    wl_time  server_timestamp;  // 服务器时间,时间校对
    wl_int32 timestamp_diff;    // 本地时间与服务器时间差

    wl_uint32 system_last_daemon_duration;  // 上次运行时长
    wl_uint32 system_last_version_num;      // 上次版本号
    wl_uint32 system_last_sdk_memory;       // 上次SDK内存
    wl_uint32 system_last_memory;           // 上次系统内存
    wl_uint32 system_last_memory_reboot;    // 上次内存重启
} repo_global;

static repo_global s_repo_global;

static WLcJSON* repo_store_read_json(wl_char* store_name);
static wl_int32 repo_store_read(wl_char* store_name, wl_char** data, wl_uint32* datalen);
static wl_int32 repo_store_save(wl_char* store_name, wl_char* data, wl_uint32 datalen);
static wl_int32 repo_dns_load(void);
static wl_int32 repo_dns_save(void);
static wl_int32 repo_system_load(void);
static wl_int32 repo_system_save(void);
static wl_int32 repo_config_load(void);
static wl_int32 repo_config_save(void);
static wl_int32 repo_group_load(void);
static wl_int32 repo_group_save(void);
static wl_int32 repo_friend_load(void);
static wl_int32 repo_friend_save(void);
static wl_int32 repo_session_load(void);
static wl_int32 repo_session_save(void);
static wl_int32 repo_ttsindex_load(void);
static wl_int32 repo_ttsindex_save(void);
static wl_int32 repo_update_session(repo_session* session);

#ifdef REPO_CREATE_TONE_HEAD
static void create_tone_head(void);
#endif

typedef enum {
    REPO_MSG_SAVE_STORE_CONFIG = 0,
    REPO_MSG_SAVE_STORE_SYSTEM,
    REPO_MSG_SAVE_STORE_DNS,
    REPO_MSG_SAVE_STORE_GROUP,
    REPO_MSG_SAVE_STORE_FRIEND,
    REPO_MSG_SAVE_STORE_SESSION,
    REPO_MSG_SAVE_STORE_TTSINDEX,
    REPO_MSG_SAVE_STORE_PLAYBACK,
} repo_module_msgid;

static wl_int32 repo_message_callback(wl_int32 msgid, void* data, wl_uint32 datalen) {
    WLLOGD("msgid %d", msgid);
    switch (msgid) {
        case REPO_MSG_SAVE_STORE_CONFIG:
            repo_store_save(REPO_CONFIG_STORE_NAME, (wl_char*)data, datalen);
            break;
        case REPO_MSG_SAVE_STORE_SYSTEM:
            repo_store_save(REPO_SYSTEM_STORE_NAME, (wl_char*)data, datalen);
            break;
        case REPO_MSG_SAVE_STORE_DNS:
            repo_store_save(REPO_DNS_STORE_NAME, (wl_char*)data, datalen);
            break;
        case REPO_MSG_SAVE_STORE_GROUP:
            repo_store_save(REPO_GROUP_STORE_NAME, (wl_char*)data, datalen);
            break;
        case REPO_MSG_SAVE_STORE_FRIEND:
            repo_store_save(REPO_FRIEND_STORE_NAME, (wl_char*)data, datalen);
            break;
        case REPO_MSG_SAVE_STORE_SESSION:
            repo_store_save(REPO_SESSION_STORE_NAME, (wl_char*)data, datalen);
            break;
        case REPO_MSG_SAVE_STORE_TTSINDEX:
            repo_store_save(REPO_TTSINDEX_STORE_NAME, (wl_char*)data, datalen);
            break;
        case REPO_MSG_SAVE_STORE_PLAYBACK: {
            wl_int32 idx = MAX_PLAYBACK_NUM;
            // 修改playback文件名 0->1 1->2 ...
            if (idx >= 2) {
                wl_char src_path[MAX_NAME_LEN] = "";
                wl_char dst_path[MAX_NAME_LEN] = "";
                for (idx = MAX_PLAYBACK_NUM - 2; idx >= 0; idx--) {
                    sprintf(src_path, "%d.ptt", idx);
                    sprintf(dst_path, "%d.ptt", idx + 1);

                    if (wl_ext_fs_exists(dst_path)) {
                        if (0 != wl_ext_fs_delete(dst_path)) {
                            WLLOGW("wl_ext_fs_delete %s fail", dst_path);
                        }else {
                            WLLOGW("wl_ext_fs_delete %s success", dst_path);
                        }
                    }

                    if (wl_ext_fs_exists(src_path)) {
                        if (0 != wl_ext_fs_rename(src_path, dst_path)) {
                            WLLOGW("wl_rename src %s to dst %s fail", src_path, dst_path);
                        } else {
                            WLLOGW("wl_rename src %s to dst %s success", src_path, dst_path);
                        }
                    }
                }
            }
            repo_store_save("0.ptt", (wl_char*)data, datalen);
            break;
        }
        default:
            WLLOGW("msgid %d not support", msgid);
            break;
    }
    return 0;
}

wl_int32 repo_init(void) {
    wl_module module;

    WLLOGI("repo_init");
    memset(&module, 0, sizeof(wl_module));
    module.moduleid = WL_MODULE_REPO;
    module.taskid   = WL_TASK_FILE;
    module.callback = repo_message_callback;
    if (0 != wl_core_register_module(&module)) {
        WLLOGW("wl_core_register_module keepalive fail");
        return -1;
    }
    memset(&s_repo_global, 0, sizeof(repo_global));

    repo_system_load();
    repo_config_load();
    repo_group_load();
    repo_friend_load();
    repo_session_load();
    repo_dns_load();
    repo_ttsindex_load();

    // 刷新固定群会话
    repo_refresh_group_sessions();
    // 清理过期会话
    repo_remove_invalid_session();
    // 添加个人管理员会话
    repo_add_single_manager_session();
    // 刷新好友会话
    repo_refresh_friend_sessions();

#ifdef REPO_CREATE_TONE_HEAD
    create_tone_head();
#endif

    return 0;
}

wl_int32 repo_end(void) {
    if (s_repo_global.config.user_config) {
        wl_free(s_repo_global.config.user_config);
        s_repo_global.config.user_config = wl_null;
    }
    wl_list_free(&s_repo_global.dns_list);
    wl_list_free(&s_repo_global.group_list);
    wl_list_free(&s_repo_global.friend_list);
    wl_list_free(&s_repo_global.session_list);
    wl_list_free(&s_repo_global.config.geofence_list);
    wl_list_free(&s_repo_global.config.button_list);
    wl_list_free(&s_repo_global.ttsindex_list);
    return 0;
}

#ifdef REPO_CREATE_TONE_HEAD
static wl_char* get_tone_buffer(wl_char* filename, wl_int32* bufferlen) {
    WL_FHANDLE fhandle = wl_fopen(filename, WL_EXT_RDONLY);
    wl_char*   buffer  = wl_null;
    wl_ssize   ret     = 0;
    // 文件不存在,新用户
    if (WL_EXT_INVALID_FS == fhandle) {
        WLLOGW("open name %s fail", filename);
        return wl_null;
    }
    *bufferlen = wl_fsize(fhandle);
    buffer     = (wl_char*)wl_malloc(*bufferlen);
    if (wl_null == buffer) {
        wl_fclose(fhandle);
        return wl_null;
    }
    ret = wl_fread(fhandle, buffer, *bufferlen);
    if (ret != *bufferlen) {
        WLLOGW("fread fail");
        wl_free(buffer);
        wl_fclose(buffer);
        return wl_null;
    }
    wl_fclose(fhandle);
    return buffer;
}

static void set_tone_buffer(WL_FHANDLE fhandle, wl_char* tonename, wl_char* buffer, wl_int32 bufferlen) {
    wl_char  desc[256] = "";
    wl_ssize ret       = wl_fwrite(fhandle, desc, strlen(desc));
    wl_int32 i         = 0;
    sprintf(desc,
            "const unsigned char %s[%d] = { 0x00, 0x00, 0x00, 0x00, '#', '!', 'O', 'P', 'U', 'S'",
            tonename,
            bufferlen);
    wl_fwrite(fhandle, desc, strlen(desc));
    for (i = 10; i < bufferlen; i++) {
        sprintf(desc, ",0x%x", (wl_uchar)(buffer[i]));
        ret = wl_fwrite(fhandle, desc, strlen(desc));
    }
    sprintf(desc, "};\n\n");
    ret = wl_fwrite(fhandle, desc, strlen(desc));
}

static void create_tone_head(void) {
    WL_FHANDLE fhandle   = WL_EXT_INVALID_FS;
    wl_char    desc[128] = "";
    wl_char*   buffer    = wl_null;
    wl_int32   bufferlen = 0;

    wl_ext_fs_delete("./tone/burst_tone.c");
    fhandle = wl_fopen("./tone/burst_tone.c", WL_CREAT | WL_TRUNC | WL_WRONLY);
    if (WL_EXT_INVALID_FS == fhandle) {
        WLLOGW("open burst tone fail");
        return;
    }

    // sprintf(desc, "#ifndef __BURST_TONE_H\n#define __BURST_TONE_H\n\n#ifdef __cplusplus\nextern \"C\"
    // {\n#endif\n\n"); wl_fwrite(fhandle, desc, strlen(desc));
    sprintf(desc, "#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n");
    wl_fwrite(fhandle, desc, strlen(desc));

    buffer = get_tone_buffer("./tone/burst_tone_start_listen.ptt", &bufferlen);
    if (buffer) {
        set_tone_buffer(fhandle, "burst_tone_start_listen", buffer, bufferlen);
        wl_free(buffer);
    }
    buffer = get_tone_buffer("./tone/burst_tone_stop_listen.ptt", &bufferlen);
    if (buffer) {
        set_tone_buffer(fhandle, "burst_tone_stop_listen", buffer, bufferlen);
        wl_free(buffer);
    }
    buffer = get_tone_buffer("./tone/burst_tone_start_record.ptt", &bufferlen);
    if (buffer) {
        set_tone_buffer(fhandle, "burst_tone_start_record", buffer, bufferlen);
        wl_free(buffer);
    }
    buffer = get_tone_buffer("./tone/burst_tone_stop_record.ptt", &bufferlen);
    if (buffer) {
        set_tone_buffer(fhandle, "burst_tone_stop_record", buffer, bufferlen);
        wl_free(buffer);
    }
    buffer = get_tone_buffer("./tone/burst_tone_deny.ptt", &bufferlen);
    if (buffer) {
        set_tone_buffer(fhandle, "burst_tone_deny", buffer, bufferlen);
        wl_free(buffer);
    }
    buffer = get_tone_buffer("./tone/burst_tone_voip_ring.ptt", &bufferlen);
    if (buffer) {
        set_tone_buffer(fhandle, "burst_tone_voip_ring", buffer, bufferlen);
        wl_free(buffer);
    }
    buffer = get_tone_buffer("./tone/burst_tone_voip_hangup.ptt", &bufferlen);
    if (buffer) {
        set_tone_buffer(fhandle, "burst_tone_voip_hangup", buffer, bufferlen);
        wl_free(buffer);
    }

    sprintf(desc, "#ifdef __cplusplus\n}\n#endif\n#endif\n");
    wl_fwrite(fhandle, desc, strlen(desc));
    wl_fclose(fhandle);
}
#endif

static void repo_save(wl_int32 msgId, WLcJSON* store) {
    wl_char* data = WLcJSON_PrintUnformatted(store);
    wl_core_send_message(WL_MODULE_REPO, msgId, data, strlen(data));
    WLcJSON_free(data);
}

static WLcJSON* repo_store_read_json(wl_char* store_name) {
    wl_char*  data    = wl_null;
    wl_uint32 datalen = 0;
    WLcJSON*  store   = wl_null;
    if (0 > repo_store_read(store_name, &data, &datalen)) {
        WLLOGW("repo_store_read %s fail", store_name);
        return WLcJSON_CreateObject();
    }
    data[datalen] = '\0';
    store         = WLcJSON_Parse(data);
    if (wl_null == store) {
        WLLOGW("store data %s parse error", data);
        wl_free(data);
        return WLcJSON_CreateObject();
    }
    wl_free(data);
    return store;
}

static wl_int32 repo_store_read(wl_char* store_name, wl_char** data, wl_uint32* datalen) {
    wl_uint32  pos     = 0;
    wl_ssize   ret     = 0;
    wl_ext_fs_handle fhandle = WL_EXT_INVALID_FS;
    WLLOGI("wl_fopen store %s", store_name);
    fhandle = wl_ext_fs_open(store_name, WL_EXT_RDONLY);
    // 文件不存在,新用户
    if (WL_EXT_INVALID_FS == fhandle) {
        WLLOGW("open repo store fail name %s", store_name);
        return -1;
    }
    *datalen = wl_ext_fs_size_by_handle(fhandle);
    *data    = (wl_char*)wl_malloc(*datalen + 1);
    if (wl_null == *data) {
        WLLOGW("wl_malloc fail datalen %d", *datalen);
        *datalen = 0;
        return -1;
    }
    while (0 < (ret = wl_ext_fs_read(fhandle, *data + pos, MAX_BUFFER_LEN))) {
        pos += ret;
    }
    WLLOGI("wl_fread store %s pos %d", store_name, pos);
    if (0 > ret) {
        WLLOGW("store data len %ld invalid", ret);
        wl_ext_fs_close(fhandle);
        wl_ext_fs_delete(store_name);
        wl_free(*data);
        *data    = wl_null;
        *datalen = 0;
        return -1;
    }
    wl_ext_fs_close(fhandle);
    WLLOGI("read %s success", store_name);
    return 0;
}

static wl_int32 repo_store_save(wl_char* store_name, wl_char* data, wl_uint32 datalen) {
    WLLOGI("store %s", store_name);
    if (wl_null != data) {
        wl_uint32  pos      = 0;
        wl_int32   left     = datalen;
        wl_ssize   writelen = 0;
        wl_ext_fs_handle fhandle  = WL_EXT_INVALID_FS;
        WLLOGI("wl_fopen store %s", store_name);
        fhandle = wl_ext_fs_open(store_name, WL_EXT_CREAT|WL_EXT_WRONLY);
        if (WL_EXT_INVALID_FS == fhandle) {
            WLLOGW("open repo store fail");
            return -1;
        }
        WLLOGI("wl_fwrite store %s left %d", store_name, left);
        while (left > 0
               && 0 < (writelen = wl_ext_fs_write(fhandle, data + pos, left > MAX_BUFFER_LEN ? MAX_BUFFER_LEN : left))) {
            left -= writelen;
            pos += writelen;
        }
        if (0 > writelen) {
            WLLOGW("writelen %ld", writelen);
            wl_ext_fs_close(fhandle);
            return -1;
        }
        WLLOGI("wl_fclose store", store_name);
        wl_ext_fs_close(fhandle);
        WLLOGI("store %s success", store_name);
        return 0;
    }
    return 0;
}

wl_int32 repo_set_appkey(repo_appkey* appkey) {
    if (appkey) {
        memcpy(&s_repo_global.appkey, appkey, sizeof(repo_appkey));
    }
    return 0;
}

wl_int32 repo_get_appkey(OUT repo_appkey* appkey) {
    if (appkey) {
        memcpy(appkey, &s_repo_global.appkey, sizeof(repo_appkey));
    }
    return 0;
}

wl_int32 repo_build_http_sign(WLcJSON* obj) {
    repo_sign     sign;
    repo_hardware hardware;
    repo_get_sign(&sign);
    repo_get_hardware(&hardware);

    WLcJSON_AddStringToObject(obj, "sign", sign.sign);
    WLcJSON_AddStringToObject(obj, "app_id", sign.appid);
    WLcJSON_AddNumberToObject(obj, "et", sign.expire_time);
    WLcJSON_AddStringToObject(obj, "product", hardware.product);
    WLcJSON_AddStringToObject(obj, "imei", hardware.imei);
    WLcJSON_AddStringToObject(obj, "version", hardware.version);
    return 0;
}

wl_int32 repo_get_sign(repo_sign* sign) {
    wl_char     cleartext[60];
    repo_appkey appkey;
    wl_int32    len = 0;
    repo_get_appkey(&appkey);
    sign->expire_time = repo_get_server_timestamp() + 120;
    len               = sprintf(cleartext, "%lld%s", sign->expire_time, appkey.appkey);
    MD5_Calculate(cleartext, len, sign->sign);
    strncpy(sign->appid, appkey.appid, sizeof(sign->appid));
    sign->appid[sizeof(sign->appid) - 1] = '\0';
    return 0;
}

wl_int32 repo_set_hardware(repo_hardware* hardware) {
    if (hardware) {
        memcpy(&s_repo_global.hardware, hardware, sizeof(repo_hardware));
    }
    return 0;
}

wl_int32 repo_get_hardware(OUT repo_hardware* hardware) {
    if (hardware) {
        memcpy(hardware, &s_repo_global.hardware, sizeof(repo_hardware));
    }
    return 0;
}

void repo_set_system_info(wl_uint32 daemon_duration, wl_uint32 memory_retoot, wl_uint32 sdk_memory, wl_uint32 memory) {
    s_repo_global.system.daemon_duration = daemon_duration;
    s_repo_global.system.memory_reboot   = memory_retoot;
    s_repo_global.system.sdk_memory      = sdk_memory;
    s_repo_global.system.memory          = memory;
    s_repo_global.system.version_num     = s_repo_global.hardware.version_number;
    repo_system_save();
}

wl_uint32 repo_get_system_last_daemon_duration(void) {
    return s_repo_global.system_last_daemon_duration;
}

wl_uint32 repo_get_system_last_version_num(void) {
    return s_repo_global.system_last_version_num;
}

wl_uint32 repo_get_system_last_sdk_memory(void) {
    return s_repo_global.system_last_sdk_memory;
}

wl_uint32 repo_get_system_last_memory(void) {
    return s_repo_global.system_last_memory;
}

wl_uint32 repo_get_system_last_memory_reboot(void) {
    return s_repo_global.system_last_memory_reboot;
}

wl_int32 repo_system_load(void) {
    WLcJSON* store      = wl_null;
    WLcJSON* object_obj = wl_null;

    store = repo_store_read_json(REPO_SYSTEM_STORE_NAME);
    if (wl_null == store) {
        WLLOGW("repo_store_read fail");
        return -1;
    }

    object_obj = WLcJSON_GetObjectItem(store, "daemon_duration");
    if (wl_null != object_obj) {
        s_repo_global.system_last_daemon_duration = (wl_uint32)WLcJSON_GetNumberValue(object_obj);
    }
    object_obj = WLcJSON_GetObjectItem(store, "version_num");
    if (wl_null != object_obj) {
        s_repo_global.system_last_version_num = (wl_uint32)WLcJSON_GetNumberValue(object_obj);
    }
    object_obj = WLcJSON_GetObjectItem(store, "sdk_memory");
    if (wl_null != object_obj) {
        s_repo_global.system_last_sdk_memory = (wl_uint32)WLcJSON_GetNumberValue(object_obj);
    }
    object_obj = WLcJSON_GetObjectItem(store, "memory");
    if (wl_null != object_obj) {
        s_repo_global.system_last_memory = (wl_uint32)WLcJSON_GetNumberValue(object_obj);
    }
    object_obj = WLcJSON_GetObjectItem(store, "memory_reboot");
    if (wl_null != object_obj) {
        s_repo_global.system_last_memory_reboot = (wl_uint32)WLcJSON_GetNumberValue(object_obj);
    }
    WLcJSON_Delete(store);
    return 0;
}

wl_int32 repo_system_save(void) {
    WLcJSON* store = WLcJSON_CreateObject();
    if (wl_null == store) {
        WLLOGW("create object fail");
        return -1;
    }
    WLcJSON_AddNumberToObject(store, "daemon_duration", s_repo_global.system.daemon_duration);
    WLcJSON_AddNumberToObject(store, "version_num", s_repo_global.system.version_num);
    WLcJSON_AddNumberToObject(store, "sdk_memory", s_repo_global.system.sdk_memory);
    WLcJSON_AddNumberToObject(store, "memory", s_repo_global.system.memory);
    WLcJSON_AddNumberToObject(store, "memory_reboot", s_repo_global.system.memory_reboot);

    repo_save(REPO_MSG_SAVE_STORE_SYSTEM, store);
    WLcJSON_Delete(store);
    return 0;
}

wl_int32 repo_config_load(void) {
    WLcJSON* store         = wl_null;
    WLcJSON* account_obj   = wl_null;
    WLcJSON* corp_obj      = wl_null;
    WLcJSON* user_obj      = wl_null;
    WLcJSON* object_obj    = wl_null;
    WLcJSON* geofences_obj = wl_null;
    WLcJSON* buttons_obj   = wl_null;
    wl_list_free(&s_repo_global.config.geofence_list);
    wl_list_free(&s_repo_global.config.button_list);
    memset(&s_repo_global.config, 0, sizeof(repo_config));
    wl_list_init(&s_repo_global.config.geofence_list);
    wl_list_init(&s_repo_global.config.button_list);
    // attach limit 默认值
    s_repo_global.config.attach_limit            = REPO_GROUP_SESSION_ATTACH_MAX;
    s_repo_global.config.location_interval       = 0;        // 默认不上报轨迹
    s_repo_global.config.location_share          = wl_true;  // 默认开共享
    s_repo_global.config.volume                  = REPO_VOLUME;
    s_repo_global.config.device_type             = REPO_DEVICE_TYPE;
    s_repo_global.config.client_type             = REPO_CLIENT_TYPE;
    s_repo_global.config.active_session_lifetime = REPO_SESSION_ACTIVE_LIFETIME;
    s_repo_global.config.tmp_session_keeptime    = REPO_SESSION_TMP_DURATION;

    store = repo_store_read_json(REPO_CONFIG_STORE_NAME);
    if (wl_null == store) {
        WLLOGW("repo_store_read fail");
        return -1;
    }

    object_obj = WLcJSON_GetObjectItem(store, "managerid");
    if (wl_null != object_obj) {
        s_repo_global.config.managerid = (wl_uint32)WLcJSON_GetNumberValue(object_obj);
    }
    object_obj = WLcJSON_GetObjectItem(store, "version");
    if (wl_null != object_obj) {
        s_repo_global.config.version = (wl_uint32)WLcJSON_GetNumberValue(object_obj);
    }
    object_obj = WLcJSON_GetObjectItem(store, "burst_session_hold");
    if (wl_null != object_obj) {
        s_repo_global.config.burst_session_hold = (wl_bool)WLcJSON_GetNumberValue(object_obj);
    }
    object_obj = WLcJSON_GetObjectItem(store, "contact_session_hold");
    if (wl_null != object_obj) {
        s_repo_global.config.contact_session_hold = (wl_bool)WLcJSON_GetNumberValue(object_obj);
    }
    object_obj = WLcJSON_GetObjectItem(store, "attach_limit");
    if (wl_null != object_obj) {
        s_repo_global.config.attach_limit = (wl_uint32)WLcJSON_GetNumberValue(object_obj);
        if (0 == s_repo_global.config.attach_limit) {
            s_repo_global.config.attach_limit = REPO_GROUP_SESSION_ATTACH_MAX;
        }
    }
    object_obj = WLcJSON_GetObjectItem(store, "device_type");
    if (wl_null != object_obj) {
        s_repo_global.config.device_type = (wl_uint32)WLcJSON_GetNumberValue(object_obj);
    }
    object_obj = WLcJSON_GetObjectItem(store, "client_type");
    if (wl_null != object_obj) {
        s_repo_global.config.client_type = (wl_uint32)WLcJSON_GetNumberValue(object_obj);
    }
    object_obj = WLcJSON_GetObjectItem(store, "active_session_lifetime");
    if (wl_null != object_obj) {
        s_repo_global.config.active_session_lifetime = (wl_uint32)WLcJSON_GetNumberValue(object_obj);
    }
    object_obj = WLcJSON_GetObjectItem(store, "tmp_session_keeptime");
    if (wl_null != object_obj) {
        s_repo_global.config.tmp_session_keeptime = (wl_uint32)WLcJSON_GetNumberValue(object_obj);
    }
    object_obj = WLcJSON_GetObjectItem(store, "default_sessionid");
    if (wl_null != object_obj) {
        s_repo_global.config.default_sessionid = (wl_uint32)WLcJSON_GetNumberValue(object_obj);
    }
    object_obj = WLcJSON_GetObjectItem(store, "default_sessiontype");
    if (wl_null != object_obj) {
        s_repo_global.config.default_sessiontype = (wl_uint32)WLcJSON_GetNumberValue(object_obj);
    }

    object_obj = WLcJSON_GetObjectItem(store, "location_interval");
    if (wl_null != object_obj) {
        s_repo_global.config.location_interval = (wl_uint32)WLcJSON_GetNumberValue(object_obj);
    }
    object_obj = WLcJSON_GetObjectItem(store, "location_share");
    if (wl_null != object_obj) {
        s_repo_global.config.location_share = (wl_bool)WLcJSON_GetNumberValue(object_obj);
    }
    object_obj = WLcJSON_GetObjectItem(store, "volume");
    if (wl_null != object_obj) {
        s_repo_global.config.volume = (wl_uint32)WLcJSON_GetNumberValue(object_obj);
    }
    object_obj = WLcJSON_GetObjectItem(store, "imsi");
    if (wl_null != object_obj && WLcJSON_IsString(object_obj)) {
        strncpy(s_repo_global.config.imsi, WLcJSON_GetStringValue(object_obj), sizeof(s_repo_global.config.imsi));
        s_repo_global.config.imsi[sizeof(s_repo_global.config.imsi) - 1] = '\0';
    }
    object_obj = WLcJSON_GetObjectItem(store, "user_config");
    if (wl_null != object_obj) {
        wl_char* content = WLcJSON_PrintUnformatted(object_obj);
        if (content) {
            wl_int32 len                     = strlen(content);
            s_repo_global.config.user_config = wl_realloc(s_repo_global.config.user_config, len + 1);
            if (s_repo_global.config.user_config) {
                strncpy(s_repo_global.config.user_config, content, len);
                s_repo_global.config.user_config[len] = '\0';
            }
            WLcJSON_free(content);
        }
    }

    account_obj = WLcJSON_GetObjectItem(store, "account");
    // 账号
    if (wl_null != account_obj) {
        WLcJSON* ac_obj  = WLcJSON_GetObjectItem(account_obj, "ac");
        WLcJSON* cc_obj  = WLcJSON_GetObjectItem(account_obj, "cc");
        WLcJSON* pwd_obj = WLcJSON_GetObjectItem(account_obj, "pwd");
        WLcJSON* md5_obj = WLcJSON_GetObjectItem(account_obj, "md5");
        if (wl_null != ac_obj && WLcJSON_IsString(ac_obj)) {
            strncpy(s_repo_global.config.account.account,
                    WLcJSON_GetStringValue(ac_obj),
                    sizeof(s_repo_global.config.account.account));
            s_repo_global.config.account.account[sizeof(s_repo_global.config.account.account) - 1] = '\0';
        }
        if (wl_null != cc_obj && WLcJSON_IsString(cc_obj)) {
            strncpy(s_repo_global.config.account.country_code,
                    WLcJSON_GetStringValue(cc_obj),
                    sizeof(s_repo_global.config.account.country_code));
            s_repo_global.config.account.country_code[sizeof(s_repo_global.config.account.country_code) - 1] = '\0';
        }
        if (wl_null != pwd_obj && WLcJSON_IsString(pwd_obj)) {
            strncpy(s_repo_global.config.account.password,
                    WLcJSON_GetStringValue(pwd_obj),
                    sizeof(s_repo_global.config.account.password));
            s_repo_global.config.account.password[sizeof(s_repo_global.config.account.password) - 1] = '\0';
        }
        if (wl_null != md5_obj) {
            s_repo_global.config.account.ismd5pwd = WLcJSON_IsTrue(md5_obj);
        }
    }

    corp_obj = WLcJSON_GetObjectItem(store, "corp");
    // 账号
    if (wl_null != corp_obj) {
        WLcJSON* obj = WLcJSON_GetObjectItem(corp_obj, "num");
        if (wl_null != obj && WLcJSON_IsString(obj)) {
            s_repo_global.config.corp.number = (wl_uint64)WLcJSON_GetNumberValue(obj);
        }
        obj = WLcJSON_GetObjectItem(corp_obj, "name");
        if (wl_null != obj && WLcJSON_IsString(obj)) {
            strncpy(
                s_repo_global.config.corp.name, WLcJSON_GetStringValue(obj), sizeof(s_repo_global.config.corp.name));
            s_repo_global.config.corp.name[sizeof(s_repo_global.config.corp.name) - 1] = '\0';
        }
        obj = WLcJSON_GetObjectItem(corp_obj, "grp_ver");
        if (wl_null != obj) {
            s_repo_global.config.corp.group_version = (wl_uint32)WLcJSON_GetNumberValue(obj);
        }
        obj = WLcJSON_GetObjectItem(corp_obj, "adr_ver");
        if (wl_null != obj) {
            s_repo_global.config.corp.address_version = (wl_uint32)WLcJSON_GetNumberValue(obj);
        }
        obj = WLcJSON_GetObjectItem(corp_obj, "tts");
        if (wl_null != obj) {
            s_repo_global.config.corp.tts = (wl_uint32)WLcJSON_GetNumberValue(obj);
        }
        obj = WLcJSON_GetObjectItem(corp_obj, "loc_share");
        if (wl_null != obj) {
            s_repo_global.config.corp.loc_share = (wl_uint32)WLcJSON_GetNumberValue(obj);
        }
        obj = WLcJSON_GetObjectItem(corp_obj, "track");
        if (wl_null != obj) {
            s_repo_global.config.corp.track = (wl_uint32)WLcJSON_GetNumberValue(obj);
        }
        obj = WLcJSON_GetObjectItem(corp_obj, "state");
        if (wl_null != obj) {
            s_repo_global.config.corp.state = (wl_uint32)WLcJSON_GetNumberValue(obj);
        }
    }

    user_obj = WLcJSON_GetObjectItem(store, "user");
    if (wl_null != user_obj) {
        WLcJSON* obj = WLcJSON_GetObjectItem(user_obj, "uid");
        if (obj) {
            s_repo_global.config.user.user.userid = (wl_uint32)WLcJSON_GetNumberValue(obj);
        }
        obj = WLcJSON_GetObjectItem(user_obj, "num");
        if (obj && WLcJSON_IsString(obj)) {
            strncpy(s_repo_global.config.user.user.number,
                    WLcJSON_GetStringValue(obj),
                    sizeof(s_repo_global.config.user.user.number));
            s_repo_global.config.user.user.number[sizeof(s_repo_global.config.user.user.number) - 1] = '\0';
        }
        obj = WLcJSON_GetObjectItem(user_obj, "sex");
        if (obj) {
            s_repo_global.config.user.user.sex = (wl_uint32)WLcJSON_GetNumberValue(obj);
        }
        obj = WLcJSON_GetObjectItem(user_obj, "nick");
        if (obj && WLcJSON_IsString(obj)) {
            strncpy(s_repo_global.config.user.user.nick,
                    WLcJSON_GetStringValue(obj),
                    sizeof(s_repo_global.config.user.user.nick));
            s_repo_global.config.user.user.nick[sizeof(s_repo_global.config.user.user.nick) - 1] = '\0';
        }
        obj = WLcJSON_GetObjectItem(user_obj, "phone");
        if (obj && WLcJSON_IsString(obj)) {
            strncpy(s_repo_global.config.user.user.phone,
                    WLcJSON_GetStringValue(obj),
                    sizeof(s_repo_global.config.user.user.phone));
            s_repo_global.config.user.user.phone[sizeof(s_repo_global.config.user.user.phone) - 1] = '\0';
        }
        obj = WLcJSON_GetObjectItem(user_obj, "cc");
        if (obj && WLcJSON_IsString(obj)) {
            strncpy(s_repo_global.config.user.user.country_code,
                    WLcJSON_GetStringValue(obj),
                    sizeof(s_repo_global.config.user.user.country_code));
            s_repo_global.config.user.user.country_code[sizeof(s_repo_global.config.user.user.country_code) - 1] = '\0';
        }
        obj = WLcJSON_GetObjectItem(user_obj, "status");
        if (obj) {
            s_repo_global.config.user.user.status = (wl_uint32)WLcJSON_GetNumberValue(obj);
        }
        obj = WLcJSON_GetObjectItem(user_obj, "type");
        if (obj) {
            s_repo_global.config.user.type = (wl_uint32)WLcJSON_GetNumberValue(obj);
        }
        obj = WLcJSON_GetObjectItem(user_obj, "created");
        if (obj) {
            s_repo_global.config.user.user.created = (wl_uint64)WLcJSON_GetNumberValue(obj);
        }
    }

    geofences_obj = WLcJSON_GetObjectItem(store, "geofences");
    if (wl_null != geofences_obj) {
        wl_int32 index = 0;
        wl_int32 count = WLcJSON_GetArraySize(geofences_obj);
        for (index = 0; index < count; index++) {
            WLcJSON* geofence_obj = WLcJSON_GetArrayItem(geofences_obj, index);
            if (wl_null != geofence_obj) {
                WLcJSON* center_obj = wl_null;
                center_obj          = WLcJSON_GetObjectItem(geofence_obj, "center");
                if (wl_null != center_obj) {
                    WLcJSON*       obj      = wl_null;
                    repo_geofence* geofence = (repo_geofence*)wl_malloc(sizeof(repo_geofence));
                    if (wl_null != geofence) {
                        memset(geofence, 0, sizeof(repo_geofence));
                        obj = WLcJSON_GetObjectItem(center_obj, "id");
                        if (obj) {
                            geofence->id = (wl_uint64)WLcJSON_GetNumberValue(obj);
                        }
                        obj = WLcJSON_GetObjectItem(center_obj, "lon");
                        if (obj) {
                            geofence->longitude = (wl_double)WLcJSON_GetNumberValue(obj);
                        }
                        obj = WLcJSON_GetObjectItem(center_obj, "lat");
                        if (obj) {
                            geofence->latitude = (wl_double)WLcJSON_GetNumberValue(obj);
                        }
                        obj = WLcJSON_GetObjectItem(geofence_obj, "radius");
                        if (obj) {
                            geofence->radius = (wl_double)WLcJSON_GetNumberValue(obj);
                        }
                        obj = WLcJSON_GetObjectItem(geofence_obj, "status");
                        if (obj) {
                            geofence->status = (wl_int32)WLcJSON_GetNumberValue(obj);
                        }
                        if (0 > wl_list_add(&s_repo_global.config.geofence_list, geofence, -1)) {
                            wl_free(geofence);
                            break;
                        }
                    }
                }
            }
        }
    }
    buttons_obj = WLcJSON_GetObjectItem(store, "buttons");
    if (wl_null != buttons_obj) {
        wl_int32 index = 0;
        wl_int32 count = WLcJSON_GetArraySize(buttons_obj);
        for (index = 0; index < count; index++) {
            WLcJSON* button_obj = WLcJSON_GetArrayItem(buttons_obj, index);
            if (wl_null != button_obj) {
                WLcJSON*     obj    = wl_null;
                repo_button* button = (repo_button*)wl_malloc(sizeof(repo_button));
                if (wl_null != button) {
                    memset(button, 0, sizeof(repo_button));
                    obj = WLcJSON_GetObjectItem(button_obj, "idx");
                    if (obj) {
                        button->idx = (wl_double)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(button_obj, "sid");
                    if (obj) {
                        button->sessionid = (wl_double)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(button_obj, "st");
                    if (obj) {
                        button->sessiontype = (wl_double)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(button_obj, "type");
                    if (obj) {
                        button->type = (wl_double)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(button_obj, "name");
                    if (obj && WLcJSON_IsString(obj)) {
                        strncpy(button->name, WLcJSON_GetStringValue(obj), sizeof(button->name));
                        button->name[sizeof(button->name) - 1] = '\0';
                    }
                    obj = WLcJSON_GetObjectItem(button_obj, "number");
                    if (obj && WLcJSON_IsString(obj)) {
                        strncpy(button->number, WLcJSON_GetStringValue(obj), sizeof(button->number));
                        button->number[sizeof(button->number) - 1] = '\0';
                    }
                    if (0 > wl_list_add(&s_repo_global.config.button_list, button, -1)) {
                        wl_free(button);
                        break;
                    }
                }
            }
        }
    }
    WLcJSON_Delete(store);
    return 0;
}

wl_int32 repo_config_save(void) {
    WLcJSON* account_obj = wl_null;
    WLcJSON* user_obj    = wl_null;
    WLcJSON* store       = WLcJSON_CreateObject();
    if (wl_null == store) {
        WLLOGW("create object fail");
        return -1;
    }
    // 管理员id
    WLcJSON_AddNumberToObject(store, "managerid", s_repo_global.config.managerid);
    // 版本
    WLcJSON_AddNumberToObject(store, "version", s_repo_global.config.version);
    // 当前会话保持
    WLcJSON_AddNumberToObject(store, "burst_session_hold", s_repo_global.config.burst_session_hold);
    // 通讯录会话保持
    WLcJSON_AddNumberToObject(store, "contact_session_hold", s_repo_global.config.contact_session_hold);
    // attach
    WLcJSON_AddNumberToObject(store, "attach_limit", s_repo_global.config.attach_limit);
    // device type
    WLcJSON_AddNumberToObject(store, "device_type", s_repo_global.config.device_type);
    // client type
    WLcJSON_AddNumberToObject(store, "client_type", s_repo_global.config.client_type);
    // active session lifetime
    WLcJSON_AddNumberToObject(store, "active_session_lifetime", s_repo_global.config.active_session_lifetime);
    // tmp session keeptime
    WLcJSON_AddNumberToObject(store, "tmp_session_keeptime", s_repo_global.config.tmp_session_keeptime);
    // location interval
    WLcJSON_AddNumberToObject(store, "location_interval", s_repo_global.config.location_interval);
    // location share
    WLcJSON_AddNumberToObject(store, "location_share", s_repo_global.config.location_share);
    // volume
    WLcJSON_AddNumberToObject(store, "volume", s_repo_global.config.volume);
    // imsi
    WLcJSON_AddStringToObject(store, "imsi", s_repo_global.config.imsi);
    // config
    if (s_repo_global.config.user_config) {
        WLcJSON* user_config_obj = WLcJSON_Parse(s_repo_global.config.user_config);
        if (wl_null != user_config_obj) {
            if (!WLcJSON_AddItemToObject(store, "user_config", user_config_obj)) {
                WLLOGW("replace item to obj fail");
                WLcJSON_Delete(store);
                WLcJSON_Delete(user_config_obj);
                return -1;
            }
        }
    }

    // 账号
    account_obj = WLcJSON_CreateObject();
    if (wl_null == account_obj) {
        WLLOGW("create obj fail");
        WLcJSON_Delete(store);
        return -1;
    }
    WLcJSON_AddStringToObject(account_obj, "ac", s_repo_global.config.account.account);
    WLcJSON_AddStringToObject(account_obj, "cc", s_repo_global.config.account.country_code);
    WLcJSON_AddStringToObject(account_obj, "pwd", s_repo_global.config.account.password);
    WLcJSON_AddBoolToObject(account_obj, "md5", s_repo_global.config.account.ismd5pwd);
    if (!WLcJSON_AddItemToObject(store, "account", account_obj)) {
        WLLOGW("replace item to obj fail");
        WLcJSON_Delete(store);
        WLcJSON_Delete(account_obj);
        return -1;
    }
    WLLOGD("account %s", s_repo_global.config.account.account);

    // 用户信息
    user_obj = WLcJSON_CreateObject();
    if (wl_null == user_obj) {
        WLLOGW("create obj fail");
        WLcJSON_Delete(store);
        return -1;
    }
    WLcJSON_AddNumberToObject(user_obj, "uid", s_repo_global.config.user.user.userid);
    WLcJSON_AddStringToObject(user_obj, "num", s_repo_global.config.user.user.number);
    WLcJSON_AddNumberToObject(user_obj, "sex", s_repo_global.config.user.user.sex);
    WLcJSON_AddStringToObject(user_obj, "nick", s_repo_global.config.user.user.nick);
    WLcJSON_AddStringToObject(user_obj, "phone", s_repo_global.config.user.user.phone);
    WLcJSON_AddStringToObject(user_obj, "cc", s_repo_global.config.user.user.country_code);
    WLcJSON_AddNumberToObject(user_obj, "status", s_repo_global.config.user.user.status);
    WLcJSON_AddNumberToObject(user_obj, "created", s_repo_global.config.user.user.created);
    if (!WLcJSON_AddItemToObject(store, "user", user_obj)) {
        WLLOGW("add item to obj fail");
        WLcJSON_Delete(store);
        WLcJSON_Delete(user_obj);
        return -1;
    }
    WLLOGD("userid %d usernumber %s", s_repo_global.config.user.user.userid, s_repo_global.config.user.user.number);

    // 围栏
    if (wl_list_size(&s_repo_global.config.geofence_list) > 0) {
        repo_geofence*   geofenceinfo  = wl_null;
        WLcJSON*         geofences_obj = wl_null;
        wl_list_iterator iterator;
        geofences_obj = WLcJSON_CreateArray();
        if (wl_null == geofences_obj) {
            WLLOGW("create object fail");
            WLcJSON_Delete(store);
            return -1;
        }
        geofenceinfo = (repo_geofence*)wl_list_get_first(&s_repo_global.config.geofence_list, &iterator);
        while (!wl_list_iterator_is_end(&iterator)) {
            WLcJSON* geofence_obj = WLcJSON_CreateObject();
            if (wl_null != geofence_obj) {
                WLcJSON_AddNumberToObject(geofence_obj, "id", geofenceinfo->id);
                WLcJSON_AddNumberToObject(geofence_obj, "lat", geofenceinfo->latitude);
                WLcJSON_AddNumberToObject(geofence_obj, "lon", geofenceinfo->longitude);
                WLcJSON_AddNumberToObject(geofence_obj, "radius", geofenceinfo->radius);
                WLcJSON_AddNumberToObject(geofence_obj, "status", geofenceinfo->status);
                if (!WLcJSON_AddItemToArray(geofences_obj, geofence_obj)) {
                    WLLOGW("add item to obj fail");
                    WLcJSON_Delete(geofence_obj);
                    break;
                }
            }
            geofenceinfo = (repo_geofence*)wl_list_get_next(&iterator);
        }
        if (!WLcJSON_AddItemToObject(store, "geofences", geofences_obj)) {
            WLLOGW("add item to obj fail");
            WLcJSON_Delete(store);
            WLcJSON_Delete(geofences_obj);
            return -1;
        }
    }

    // 按键定义
    if (wl_list_size(&s_repo_global.config.button_list) > 0) {
        repo_button*     buttoninfo  = wl_null;
        WLcJSON*         buttons_obj = wl_null;
        wl_list_iterator iterator;
        buttons_obj = WLcJSON_CreateArray();
        if (wl_null == buttons_obj) {
            WLLOGW("create object fail");
            WLcJSON_Delete(store);
            return -1;
        }
        buttoninfo = (repo_button*)wl_list_get_first(&s_repo_global.config.geofence_list, &iterator);
        while (!wl_list_iterator_is_end(&iterator)) {
            WLcJSON* button_obj = WLcJSON_CreateObject();
            if (wl_null != button_obj) {
                WLcJSON_AddNumberToObject(button_obj, "idx", buttoninfo->idx);
                WLcJSON_AddNumberToObject(button_obj, "sid", buttoninfo->sessionid);
                WLcJSON_AddNumberToObject(button_obj, "st", buttoninfo->sessiontype);
                WLcJSON_AddNumberToObject(button_obj, "type", buttoninfo->type);
                WLcJSON_AddStringToObject(button_obj, "name", buttoninfo->name);
                WLcJSON_AddStringToObject(button_obj, "number", buttoninfo->number);
                if (!WLcJSON_AddItemToArray(buttons_obj, button_obj)) {
                    WLLOGW("add item to obj fail");
                    WLcJSON_Delete(button_obj);
                    break;
                }
            }
            buttoninfo = (repo_button*)wl_list_get_next(&iterator);
        }
        if (!WLcJSON_AddItemToObject(store, "buttons", buttons_obj)) {
            WLLOGW("add item to obj fail");
            WLcJSON_Delete(store);
            WLcJSON_Delete(buttons_obj);
            return -1;
        }
    }

    repo_save(REPO_MSG_SAVE_STORE_CONFIG, store);
    WLcJSON_Delete(store);
    return 0;
}

wl_int32 repo_set_account(repo_account* account) {
    // 账号变更,清理旧账号数据
    if (0 != strncmp(s_repo_global.config.account.account, account->account, sizeof(account->account))) {
        wl_list_free(&s_repo_global.group_list);
        wl_list_free(&s_repo_global.friend_list);
        wl_list_free(&s_repo_global.session_list);
        memset(&s_repo_global.config.account, 0, sizeof(repo_account));
        // 删除数据文件
        wl_ext_fs_delete(REPO_GROUP_STORE_NAME);
        wl_ext_fs_delete(REPO_FRIEND_STORE_NAME);
        wl_ext_fs_delete(REPO_SESSION_STORE_NAME);
        wl_ext_fs_delete(REPO_CONFIG_STORE_NAME);

        // 复制账号信息
        memcpy(&s_repo_global.config.account, account, sizeof(repo_account));
    }
    // 存储数据
    return repo_config_save();
}

wl_int32 repo_get_account(OUT repo_account* account) {
    memcpy(account, &s_repo_global.config.account, sizeof(repo_account));
    return 0;
}

wl_int32 repo_set_corp(repo_corp* corp) {
    memcpy(&s_repo_global.corp_info, corp, sizeof(repo_corp));
    return 0;
}

wl_int32 repo_get_corp(OUT repo_corp* corp) {
    memcpy(corp, &s_repo_global.corp_info, sizeof(repo_corp));
    return 0;
}

wl_bool repo_is_corp_user(void) {
    if ((s_repo_global.config.user.type & 0xF0) == 0x10) {
        return wl_true;
    }
    return wl_false;
}

wl_uint32 repo_get_corp_address_version(void) {
    return s_repo_global.corp_address_version;
}

wl_int32 repo_set_corp_address_version(wl_uint32 version) {
    s_repo_global.corp_address_version = version;
    return 0;
}

wl_int32 repo_set_corp_address_list(wl_uint32 version, repo_friend* friends, wl_uint32 count) {
    wl_uint32 index                    = 0;
    s_repo_global.corp_address_version = version;
    wl_list_free(&s_repo_global.friend_list);
    for (index = 0; index < count && index < REPO_FRIEND_MAX; index++) {
        if (friends[index].friend.userid != s_repo_global.config.user.user.userid) {
            repo_friend* friendinfo = (repo_friend*)wl_malloc(sizeof(repo_friend));
            if (wl_null != friendinfo) {
                memcpy(friendinfo, &friends[index], sizeof(repo_friend));
                if (0 > wl_list_add(&s_repo_global.friend_list, friendinfo, -1)) {
                    wl_free(friendinfo);
                    return -1;
                }
            }
        }
    }
    return repo_friend_save();
}

wl_uint32 repo_get_corp_group_version(void) {
    return s_repo_global.corp_group_version;
}

wl_int32 repo_set_corp_groups(wl_uint32 version, repo_group* groups, wl_uint32 count) {
    wl_uint32 index                  = 0;
    s_repo_global.corp_group_version = version;
    wl_list_free(&s_repo_global.group_list);  // 清除本地数据
    for (index = 0; index < count; index++) {
        repo_group* groupinfo = (repo_group*)wl_malloc(sizeof(repo_group));
        if (wl_null == groupinfo) {
            WLLOGW("malloc fail");
            break;
        }
        memcpy(groupinfo, &groups[index], sizeof(repo_group));
        // 新群添加尾部
        if (0 > wl_list_add(&s_repo_global.group_list, groupinfo, -1)) {
            WLLOGW("list add fail");
            break;
        }
        // 群数超过限制,删除首部旧群
        if (wl_list_size(&s_repo_global.group_list) > REPO_GROUP_MAX) {
            groupinfo = wl_list_get(&s_repo_global.group_list, 0);
            if (wl_null != groupinfo) {
                wl_free(groupinfo);
                wl_list_remove(&s_repo_global.group_list, 0);
            }
        }
    }
    repo_group_save();
    return 0;
}

wl_uint32 repo_get_session_message_session_type(wl_uint64 sessionid, wl_uint32 session_type) {
    // 个人会话
    if (WLSDK_SESSION_TYPE_SINGLE == session_type) {
        repo_friend friend;
        if (0 == repo_get_friend((wl_uint32)sessionid, &friend)) {
            // 个人好友
            if (friend.type == REPO_FRIEND_SINGLE) {
                return REPO_SESSION_TYPE_SINGLE;
            }
            // 企业成员
            else {
                return REPO_SESSION_TYPE_CORP_SINGLE;
            }
        }
    }
    // 群会话
    else if (WLSDK_SESSION_TYPE_GROUP == session_type) {
        repo_group group;
        if (0 == repo_get_group(sessionid, &group)) {
            // 个人群
            if (group.group.group_type == REPO_GROUP_NORMAL || group.group.group_type == REPO_GROUP_TMP) {
                return REPO_SESSION_TYPE_GROUP;
            }
            // 企业群
            else {
                if (group.group.group_type == REPO_GROUP_CORP_NORMAL) {
                    return REPO_SESSION_TYPE_CORP_GROUP;
                } else if (group.group.group_type == REPO_GROUP_CORP_TMP) {
                    return REPO_SESSION_TYPE_CORP_GROUP_TMP;
                }
            }
        }
    }
    // 其他
    return session_type;
}

wl_int32 repo_set_managerid(wl_uint32 userid) {
    s_repo_global.config.managerid = userid;
    return repo_config_save();
}

wl_uint32 repo_get_managerid(void) {
    return s_repo_global.config.managerid;
}

wl_int32 repo_set_login_url(repo_url* url) {
    if (url) {
        memcpy(&s_repo_global.login_url, url, sizeof(repo_url));
    }
    return 0;
}

wl_int32 repo_get_login_url(OUT repo_url* url) {
    if (url) {
        memcpy(url, &s_repo_global.login_url, sizeof(repo_url));
    }
    return 0;
}

wl_int32 repo_set_web_server(repo_server* server) {
    if (server) {
        memcpy(&s_repo_global.web_server, server, sizeof(repo_server));
    }
    return 0;
}

wl_int32 repo_get_web_server(OUT repo_server* server) {
    if (server) {
        memcpy(server, &s_repo_global.web_server, sizeof(repo_server));
    }
    return 0;
}

wl_int32 repo_set_token(wl_char* token) {
    if (token) {
        strncpy(s_repo_global.token, token, sizeof(s_repo_global.token));
        s_repo_global.token[sizeof(s_repo_global.token) - 1] = '\0';
    }
    return 0;
}

wl_int32 repo_get_token(OUT wl_char* token) {
    if (token) {
        strcpy(token, s_repo_global.token);
    }
    return 0;
}

wl_time repo_get_server_timestamp(void) {
    wl_time  utctime = wl_utctime();
    wl_int32 diff    = s_repo_global.server_timestamp - utctime;
    // 本地时间与服务器时间差值在60秒内有效
    if (diff > -60 && diff < 60) {
        return utctime;
    } else {
        WLLOGD("local %d server %d adjust %d new diff %d old diff %d",
               utctime,
               s_repo_global.server_timestamp,
               utctime + s_repo_global.timestamp_diff,
               diff,
               s_repo_global.timestamp_diff);
        // 时间差超过校准上限
        if (diff > s_repo_global.timestamp_diff + 120 || diff < s_repo_global.timestamp_diff - 120) {
            return s_repo_global.server_timestamp;
        } else {
            return utctime + s_repo_global.timestamp_diff;
        }
    }
}

void repo_set_server_timestamp(wl_time timestamp) {
    if (timestamp > 0) {
        wl_time  utctime               = wl_utctime();
        wl_int32 diff                  = timestamp - utctime;  // 本地与服务器时间差
        s_repo_global.server_timestamp = timestamp;
        WLLOGD("local %d server %d new diff %d old diff %d",
               utctime,
               s_repo_global.server_timestamp,
               diff,
               s_repo_global.timestamp_diff);
        // 如果时间差相差超过120秒,更新
        if ((diff > s_repo_global.timestamp_diff + 120) || (diff < s_repo_global.timestamp_diff - 120)) {
            s_repo_global.timestamp_diff = diff;
        }
    }
}

void repo_adjust_server_timestamp(wl_int32 seconds) {
    s_repo_global.server_timestamp += seconds;
    repo_set_server_timestamp(s_repo_global.server_timestamp);
}

wl_int32 repo_set_user_config(wl_char* config) {
    if (config) {
        wl_int32 len                     = strlen(config);
        s_repo_global.config.user_config = wl_realloc(s_repo_global.config.user_config, len + 1);
        if (s_repo_global.config.user_config) {
            strncpy(s_repo_global.config.user_config, config, len);
            s_repo_global.config.user_config[len] = '\0';
        }
    } else {
        if (s_repo_global.config.user_config) {
            wl_free(s_repo_global.config.user_config);
            s_repo_global.config.user_config = wl_null;
        }
    }
    return repo_config_save();
}

wl_char* repo_get_user_config(void) {
    return s_repo_global.config.user_config;
}

wl_int32 repo_set_extension_config(wl_char* config) {
    WLcJSON* config_obj        = WLcJSON_Parse(config);
    WLcJSON* readonly_obj      = wl_null;
    WLcJSON* trace_obj         = wl_null;
    WLcJSON* geofences_obj     = wl_null;
    WLcJSON* location_mode_obj = wl_null;
    WLcJSON* obj               = wl_null;
    if (wl_null == config_obj) {
        WLLOGW("config invalid");
        return -1;
    }
    wl_list_free(&s_repo_global.config.geofence_list);
    wl_list_init(&s_repo_global.config.geofence_list);
    // s_repo_global.config.attach_limit            = REPO_GROUP_SESSION_ATTACH_MAX;
    // s_repo_global.config.location_interval       = 0;
    // s_repo_global.config.location_share          = wl_true;
    // s_repo_global.config.volume                  = REPO_VOLUME;
    // s_repo_global.config.device_type             = REPO_DEVICE_TYPE;
    // s_repo_global.config.client_type             = REPO_CLIENT_TYPE;
    // s_repo_global.config.active_session_lifetime = REPO_SESSION_ACTIVE_LIFETIME;
    // s_repo_global.config.tmp_session_keeptime    = REPO_SESSION_TMP_DURATION;
    // s_repo_global.config.default_sessionid       = 0;
    // s_repo_global.config.default_sessiontype     = 0;

    obj = WLcJSON_GetObjectItem(config_obj, "version");
    if (wl_null != obj) {
        s_repo_global.config.version = (wl_uint32)WLcJSON_GetNumberValue(obj);
    }
    readonly_obj = WLcJSON_GetObjectItem(config_obj, "readonly");
    if (wl_null != readonly_obj) {
        obj = WLcJSON_GetObjectItem(readonly_obj, "burst_session_hold");
        if (wl_null != obj) {
            s_repo_global.config.burst_session_hold = (wl_bool)WLcJSON_GetNumberValue(obj);
        }
        obj = WLcJSON_GetObjectItem(readonly_obj, "contact_session_hold");
        if (wl_null != obj) {
            s_repo_global.config.contact_session_hold = (wl_bool)WLcJSON_GetNumberValue(obj);
        }
        obj = WLcJSON_GetObjectItem(readonly_obj, "attach_limit");
        if (wl_null != obj) {
            s_repo_global.config.attach_limit = (wl_uint32)WLcJSON_GetNumberValue(obj);
            if (0 == s_repo_global.config.attach_limit) {
                s_repo_global.config.attach_limit = REPO_GROUP_SESSION_ATTACH_MAX;
            }
        }
        obj = WLcJSON_GetObjectItem(readonly_obj, "device_type");
        if (wl_null != obj) {
            s_repo_global.config.device_type = (wl_uint32)WLcJSON_GetNumberValue(obj);
        }
        obj = WLcJSON_GetObjectItem(readonly_obj, "client_type");
        if (wl_null != obj) {
            s_repo_global.config.client_type = (wl_uint32)WLcJSON_GetNumberValue(obj);
        }
        obj = WLcJSON_GetObjectItem(readonly_obj, "active_session_lifetime");
        if (wl_null != obj) {
            s_repo_global.config.active_session_lifetime = (wl_uint32)WLcJSON_GetNumberValue(obj);
        }
        obj = WLcJSON_GetObjectItem(readonly_obj, "tmp_session_keeptime");
        if (wl_null != obj) {
            s_repo_global.config.tmp_session_keeptime = (wl_uint32)WLcJSON_GetNumberValue(obj);
        }
    }
    obj = WLcJSON_GetObjectItem(config_obj, "default_sessionid");
    if (wl_null != obj) {
        s_repo_global.config.default_sessionid = (wl_uint32)WLcJSON_GetNumberValue(obj);
    }
    obj = WLcJSON_GetObjectItem(config_obj, "default_sessiontype");
    if (wl_null != obj) {
        s_repo_global.config.default_sessiontype = (wl_uint32)WLcJSON_GetNumberValue(obj);
    }
    location_mode_obj = WLcJSON_GetObjectItem(config_obj, "location_mode");
    if (wl_null != location_mode_obj) {
        WLcJSON* interval_obj = WLcJSON_GetObjectItem(location_mode_obj, "interval");
        if (wl_null != interval_obj) {
            s_repo_global.config.location_interval = (wl_uint32)WLcJSON_GetNumberValue(interval_obj);
        }
    }
    obj = WLcJSON_GetObjectItem(config_obj, "location_share");
    if (wl_null != obj) {
        s_repo_global.config.location_share = (wl_bool)WLcJSON_GetNumberValue(obj);
    }
    trace_obj = WLcJSON_GetObjectItem(config_obj, "trace");
    if (wl_null != trace_obj) {
        WLcJSON* trace_switch_obj = WLcJSON_GetObjectItem(trace_obj, "trace_switch");
        WLcJSON* trace_level_obj  = WLcJSON_GetObjectItem(trace_obj, "trace_level");
        if (trace_switch_obj && trace_level_obj) {
            if (WLcJSON_IsNumber(trace_switch_obj) && WLcJSON_IsNumber(trace_level_obj)) {
                wl_trace_config((wl_bool)WLcJSON_GetNumberValue(trace_switch_obj),
                                (wl_uint32)WLcJSON_GetNumberValue(trace_level_obj));
            } else {
                wl_trace_config(wl_false, WLLOG_ERROR_LEVEL);
            }
        } else {
            wl_trace_config(wl_false, WLLOG_ERROR_LEVEL);
        }
    }
    obj = WLcJSON_GetObjectItem(config_obj, "volume");
    if (wl_null != obj) {
        s_repo_global.config.volume = (wl_uint32)WLcJSON_GetNumberValue(obj);
    }
    geofences_obj = WLcJSON_GetObjectItem(config_obj, "geofences");
    if (wl_null != geofences_obj) {
        wl_int32 index = 0;
        wl_int32 count = WLcJSON_GetArraySize(geofences_obj);
        for (index = 0; index < count; index++) {
            WLcJSON* geofence_obj = WLcJSON_GetArrayItem(geofences_obj, index);
            if (wl_null != geofence_obj) {
                WLcJSON*       obj      = wl_null;
                repo_geofence* geofence = (repo_geofence*)wl_malloc(sizeof(repo_geofence));
                if (wl_null != geofence) {
                    memset(geofence, 0, sizeof(repo_geofence));
                    obj = WLcJSON_GetObjectItem(geofence_obj, "id");
                    if (obj) {
                        geofence->id = (wl_uint64)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(geofence_obj, "longitude");
                    if (obj) {
                        char* value = WLcJSON_GetStringValue(obj);
                        if (value) {
                            geofence->longitude = atof(value);
                        }
                    }
                    obj = WLcJSON_GetObjectItem(geofence_obj, "latitude");
                    if (obj) {
                        char* value = WLcJSON_GetStringValue(obj);
                        if (value) {
                            geofence->latitude = atof(value);
                        }
                    }
                    obj = WLcJSON_GetObjectItem(geofence_obj, "radius");
                    if (obj) {
                        geofence->radius = (wl_double)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(geofence_obj, "enable");
                    if (obj) {
                        geofence->status = (wl_int32)WLcJSON_GetNumberValue(obj);
                    }
                    if (0 > wl_list_add(&s_repo_global.config.geofence_list, geofence, -1)) {
                        wl_free(geofence);
                        break;
                    }
                }
            }
        }
    }
    obj = WLcJSON_GetObjectItem(config_obj, "imsi");
    if (wl_null != obj && WLcJSON_IsString(obj)) {
        strncpy(s_repo_global.config.imsi, WLcJSON_GetStringValue(obj), sizeof(s_repo_global.config.imsi));
        s_repo_global.config.imsi[sizeof(s_repo_global.config.imsi) - 1] = '\0';
    }
    WLcJSON_Delete(config_obj);
    return repo_config_save();
}

wl_uint32 repo_get_extension_config_version(void) {
    return s_repo_global.config.version;
}

wl_uint32 repo_get_extension_config_location_interval(void) {
    if (0 != s_repo_global.config.location_interval) {
        if (s_repo_global.config.location_interval < REPO_LOCATION_INTERVAL_MIN) {
            s_repo_global.config.location_interval = REPO_LOCATION_INTERVAL_MIN;
        } else if (s_repo_global.config.location_interval > REPO_LOCATION_INTERVAL_MAX) {
            s_repo_global.config.location_interval = REPO_LOCATION_INTERVAL_MAX;
        }
    }
    return s_repo_global.config.location_interval;
}

wl_uint32 repo_get_extension_config_volume(void) {
    return s_repo_global.config.volume;
}

wl_list* repo_get_extension_config_geofence_list(void) {
    return &s_repo_global.config.geofence_list;
}

void repo_set_extension_config_geofence_transition_status(wl_uint64 id, wl_uint32 transition_status) {
    // 围栏
    if (wl_list_size(&s_repo_global.config.geofence_list) > 0) {
        repo_geofence*   geofenceinfo = wl_null;
        wl_list_iterator iterator;
        geofenceinfo = (repo_geofence*)wl_list_get_first(&s_repo_global.config.geofence_list, &iterator);
        while (!wl_list_iterator_is_end(&iterator)) {
            if (geofenceinfo->id == id) {
                geofenceinfo->transition_status = transition_status;
                break;
            }
            geofenceinfo = (repo_geofence*)wl_list_get_next(&iterator);
        }
    }
}

wl_uint32 repo_get_extension_config_attach_limit(void) {
    if (0 == s_repo_global.config.attach_limit) {
        return REPO_GROUP_SESSION_ATTACH_MAX;
    }
    return s_repo_global.config.attach_limit;
}

wl_uint32 repo_get_extension_config_device_type(void) {
    switch (s_repo_global.config.device_type) {
        case REPO_DEVICE_SLAVE:
        case REPO_DEVICE_SUB:
            return s_repo_global.config.device_type;
        default:
            return REPO_DEVICE_TYPE;
    }
}

wl_uint32 repo_get_extension_config_client_type(void) {
    switch (s_repo_global.config.client_type) {
        case REPO_CLIENT_SMART:
        case REPO_CLIENT_EMBEDDED:
        case REPO_CLIENT_EMBEDDED_ATTACH:
            return s_repo_global.config.client_type;
        default:
            return REPO_CLIENT_TYPE;
    }
}

wl_int32 repo_get_extension_config_imsi(OUT wl_char* imsi) {
    if (imsi) {
        strcpy(imsi, s_repo_global.config.imsi);
    }
    return 0;
}

wl_int32 repo_get_extension_config_button(wl_int32 idx, repo_button* button) {
    wl_list_iterator iterator;
    repo_button*     buttoninfo = (repo_button*)wl_list_get_first(&s_repo_global.config.button_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (idx == buttoninfo->idx) {
            memcpy(button, buttoninfo, sizeof(repo_button));
            return 0;
        }
        buttoninfo = (repo_button*)wl_list_get_next(&iterator);
    }
    return -1;
}

wl_bool repo_get_extension_config_location_share(void) {
    // 企业用户
    if (repo_is_corp_user()) {
        return s_repo_global.corp_info.loc_share;
    }
    return s_repo_global.config.location_share;
}

wl_int32 repo_set_selfinfo(repo_user* userinfo) {
    memcpy(&s_repo_global.config.user, userinfo, sizeof(repo_user));
    return repo_config_save();
}

wl_int32 repo_get_selfinfo(OUT repo_user* userinfo) {
    memcpy(userinfo, &s_repo_global.config.user, sizeof(repo_user));
    return 0;
}

void repo_clear_account(void) {
    wl_list_free(&s_repo_global.group_list);
    wl_list_free(&s_repo_global.friend_list);
    wl_list_free(&s_repo_global.session_list);

    // 删除数据文件
    wl_ext_fs_delete(REPO_CONFIG_STORE_NAME);
    wl_ext_fs_delete(REPO_GROUP_STORE_NAME);
    wl_ext_fs_delete(REPO_FRIEND_STORE_NAME);
    wl_ext_fs_delete(REPO_SESSION_STORE_NAME);

    repo_config_load();
}

wl_uint32 repo_get_userid(void) {
    return s_repo_global.config.user.user.userid;
}

wl_int32 repo_set_login_state(wl_int32 state) {
    s_repo_global.login_state = state;
    return 0;
}

wl_int32 repo_get_login_state(void) {
    return s_repo_global.login_state;
}

wl_int32 repo_set_extension_state(wl_int32 state) {
    s_repo_global.extension_state = state;
    return 0;
}

wl_int32 repo_get_extension_state(void) {
    return s_repo_global.extension_state;
}

wl_int32 repo_set_extension_bind(repo_extension_bind* extension_bind) {
    memcpy(&s_repo_global.extension_bind, extension_bind, sizeof(repo_extension_bind));
    return 0;
}

wl_int32 repo_get_extension_bind(repo_extension_bind* extension_bind) {
    memcpy(extension_bind, &s_repo_global.extension_bind, sizeof(repo_extension_bind));
    return 0;
}

wl_bool repo_is_normal_group(wl_uint64 groupid) {
    repo_group group;
    if (0 == repo_get_group(groupid, &group)) {
        if (WLSDK_GROUP_NORMAL == group.group.group_type) {
            return wl_true;
        }
    }
    return wl_false;
}

wl_int32 repo_group_load(void) {
    WLcJSON* store       = wl_null;
    WLcJSON* version_obj = wl_null;
    WLcJSON* groups_obj  = wl_null;
    wl_list_free(&s_repo_global.group_list);
    wl_list_init(&s_repo_global.group_list);

    store = repo_store_read_json(REPO_GROUP_STORE_NAME);
    if (wl_null == store) {
        WLLOGW("repo_store_read fail");
        return -1;
    }
    version_obj = WLcJSON_GetObjectItem(store, "version");
    if (wl_null != version_obj) {
        s_repo_global.corp_group_version = (wl_uint32)WLcJSON_GetNumberValue(version_obj);
    }
    groups_obj = WLcJSON_GetObjectItem(store, "groups");
    if (wl_null != groups_obj) {
        wl_int32 index = 0;
        wl_int32 count = WLcJSON_GetArraySize(groups_obj);
        for (index = 0; index < count; index++) {
            WLcJSON* group_obj = WLcJSON_GetArrayItem(groups_obj, index);
            if (wl_null != group_obj) {
                repo_group* groupinfo = (repo_group*)wl_malloc(sizeof(repo_group));
                if (wl_null != groupinfo) {
                    WLcJSON* obj = wl_null;
                    memset(groupinfo, 0, sizeof(repo_group));  // 每次重启重新同步
                    obj = WLcJSON_GetObjectItem(group_obj, "gid");
                    if (obj) {
                        groupinfo->group.groupid = (wl_uint32)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(group_obj, "gnm");
                    if (obj && WLcJSON_IsString(obj)) {
                        strncpy(groupinfo->group.group_name,
                                WLcJSON_GetStringValue(obj),
                                sizeof(groupinfo->group.group_name));
                        groupinfo->group.group_name[sizeof(groupinfo->group.group_name) - 1] = '\0';
                    }
                    obj = WLcJSON_GetObjectItem(group_obj, "gnu");
                    if (obj && WLcJSON_IsString(obj)) {
                        strncpy(groupinfo->group.group_number,
                                WLcJSON_GetStringValue(obj),
                                sizeof(groupinfo->group.group_number));
                        groupinfo->group.group_number[sizeof(groupinfo->group.group_number) - 1] = '\0';
                    }
                    obj = WLcJSON_GetObjectItem(group_obj, "goid");
                    if (obj) {
                        groupinfo->group.ownerid = (wl_uint32)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(group_obj, "gbt");
                    if (obj) {
                        groupinfo->group.bursttype = (wl_uint16)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(group_obj, "gsts");
                    if (obj) {
                        groupinfo->group.shutupstatus = (wl_uint16)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(group_obj, "gsds");
                    if (obj) {
                        groupinfo->group.shieldstatus = (wl_uint16)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(group_obj, "mtts");
                    if (obj) {
                        groupinfo->group.tts = (wl_uint16)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(group_obj, "mls");
                    if (obj) {
                        groupinfo->group.locationshare = (wl_uint16)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(group_obj, "mv");
                    if (obj) {
                        groupinfo->group.member_version = (wl_uint32)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(group_obj, "mc");
                    if (obj) {
                        groupinfo->group.member_count = (wl_uint16)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(group_obj, "mt");
                    if (obj) {
                        groupinfo->group.member_type = (wl_uint16)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(group_obj, "mp");
                    if (obj) {
                        groupinfo->group.member_prority = (wl_uint16)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(group_obj, "msts");
                    if (obj) {
                        groupinfo->group.member_shutupstatus = (wl_uint16)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(group_obj, "mtt");
                    if (obj) {
                        groupinfo->group.member_shutuptimeout = (wl_uint64)WLcJSON_GetNumberValue(obj);
                    }
                    if (0 > wl_list_add(&s_repo_global.group_list, groupinfo, -1)) {
                        wl_free(groupinfo);
                        break;
                    }
                }
            }
        }
    }
    WLcJSON_Delete(store);
    return 0;
}

wl_int32 repo_group_save(void) {
    wl_list_iterator iterator;
    repo_group*      groupinfo  = wl_null;
    WLcJSON*         groups_obj = wl_null;
    WLcJSON*         store      = WLcJSON_CreateObject();
    if (wl_null == store) {
        WLLOGW("create object fail");
        return -1;
    }
    groups_obj = WLcJSON_CreateArray();
    if (wl_null == groups_obj) {
        WLLOGW("create object fail");
        WLcJSON_Delete(store);
        return -1;
    }

    groupinfo = (repo_group*)wl_list_get_first(&s_repo_global.group_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        // if (WLSDK_GROUP_NORMAL == groupinfo->group.group_type) {
        WLcJSON* group_obj = WLcJSON_CreateObject();
        if (wl_null != group_obj) {
            WLcJSON_AddNumberToObject(group_obj, "gid", groupinfo->group.groupid);
            WLcJSON_AddNumberToObject(group_obj, "gv", groupinfo->group.group_version);
            WLcJSON_AddStringToObject(group_obj, "gnm", groupinfo->group.group_name);
            WLcJSON_AddStringToObject(group_obj, "gnu", groupinfo->group.group_number);
            WLcJSON_AddNumberToObject(group_obj, "goid", groupinfo->group.ownerid);
            WLcJSON_AddNumberToObject(group_obj, "gbt", groupinfo->group.bursttype);
            WLcJSON_AddNumberToObject(group_obj, "gsts", groupinfo->group.shutupstatus);
            WLcJSON_AddNumberToObject(group_obj, "gsds", groupinfo->group.shieldstatus);
            WLcJSON_AddNumberToObject(group_obj, "mtts", groupinfo->group.tts);
            WLcJSON_AddNumberToObject(group_obj, "mls", groupinfo->group.locationshare);
            WLcJSON_AddNumberToObject(group_obj, "mv", groupinfo->group.member_version);
            WLcJSON_AddNumberToObject(group_obj, "mc", groupinfo->group.member_count);
            WLcJSON_AddNumberToObject(group_obj, "mt", groupinfo->group.member_type);
            WLcJSON_AddNumberToObject(group_obj, "mp", groupinfo->group.member_prority);
            WLcJSON_AddNumberToObject(group_obj, "msts", groupinfo->group.member_shutupstatus);
            WLcJSON_AddNumberToObject(group_obj, "mtt", groupinfo->group.member_shutuptimeout);
            WLcJSON_AddItemToArray(groups_obj, group_obj);
        }
        // }
        groupinfo = (repo_group*)wl_list_get_next(&iterator);
    }
    WLcJSON_AddNumberToObject(store, "version", s_repo_global.corp_group_version);
    if (!WLcJSON_AddItemToObject(store, "groups", groups_obj)) {
        WLLOGW("add item to obj fail");
        WLcJSON_Delete(store);
        WLcJSON_Delete(groups_obj);
        return -1;
    }
    repo_save(REPO_MSG_SAVE_STORE_GROUP, store);
    WLcJSON_Delete(store);
    // 刷新固定群会话
    repo_refresh_group_sessions();
    return 0;
}

wl_list* repo_get_group_list(void) {
    return &s_repo_global.group_list;
}

void repo_clear_group_list(void) {
    wl_list_free(&s_repo_global.group_list);
    wl_list_init(&s_repo_global.group_list);
    wl_ext_fs_delete(REPO_GROUP_STORE_NAME);
}

wl_int32 repo_add_groups(repo_group* groups, wl_uint32 count) {
    wl_uint32 index = 0;
    for (index = 0; index < count; index++) {
        wl_list_iterator iterator;

        repo_group* groupinfo = (repo_group*)wl_list_get_first(&s_repo_global.group_list, &iterator);
        // 删除已添加群
        while (!wl_list_iterator_is_end(&iterator)) {
            if (groups[index].group.groupid == groupinfo->group.groupid) {
                wl_free(groupinfo);
                wl_list_iterator_remove(&iterator);
                break;
            }
            groupinfo = (repo_group*)wl_list_get_next(&iterator);
        }
        groupinfo = (repo_group*)wl_malloc(sizeof(repo_group));
        if (wl_null == groupinfo) {
            WLLOGW("malloc fail");
            break;
        }
        memcpy(groupinfo, &groups[index], sizeof(repo_group));
        // 新群添加尾部
        if (0 > wl_list_add(&s_repo_global.group_list, groupinfo, -1)) {
            WLLOGW("list add fail");
            break;
        }
        // 群数超过限制,删除首部旧群
        if (wl_list_size(&s_repo_global.group_list) > REPO_GROUP_MAX) {
            groupinfo = wl_list_get(&s_repo_global.group_list, 0);
            if (wl_null != groupinfo) {
                wl_free(groupinfo);
                wl_list_remove(&s_repo_global.group_list, 0);
            }
        }
    }
    repo_group_save();
    return 0;
}

wl_int32 repo_del_groups(wl_uint64* groupIds, wl_uint32 count) {
    wl_uint32 index = 0;
    for (index = 0; index < count; index++) {
        wl_list_iterator iterator;
        repo_group*      groupinfo = (repo_group*)wl_list_get_first(&s_repo_global.group_list, &iterator);
        while (!wl_list_iterator_is_end(&iterator)) {
            if (groupIds[index] == groupinfo->group.groupid) {
                wl_free(groupinfo);
                wl_list_iterator_remove(&iterator);
                break;
            }
            groupinfo = (repo_group*)wl_list_get_next(&iterator);
        }
    }
    repo_group_save();
    return 0;
}

wl_int32 repo_get_group(wl_uint64 groupId, repo_group* group) {
    wl_list_iterator iterator;
    repo_group*      groupinfo = (repo_group*)wl_list_get_first(&s_repo_global.group_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (groupId == groupinfo->group.groupid) {
            memcpy(group, groupinfo, sizeof(repo_group));
            return 0;
        }
        groupinfo = (repo_group*)wl_list_get_next(&iterator);
    }
    return -1;
}

wl_int32 repo_friend_load(void) {
    WLcJSON* store               = wl_null;
    WLcJSON* version_obj         = wl_null;
    WLcJSON* friends_obj         = wl_null;
    s_repo_global.friend_updated = 0;  // 每次重启从新同步
    wl_list_free(&s_repo_global.friend_list);
    wl_list_init(&s_repo_global.friend_list);
    store = repo_store_read_json(REPO_FRIEND_STORE_NAME);
    if (wl_null == store) {
        WLLOGW("repo_store_read fail");
        return -1;
    }
    version_obj = WLcJSON_GetObjectItem(store, "version");
    if (wl_null != version_obj) {
        s_repo_global.corp_address_version = (wl_uint32)WLcJSON_GetNumberValue(version_obj);
    }
    friends_obj = WLcJSON_GetObjectItem(store, "friends");
    if (wl_null != friends_obj) {
        wl_int32 index = 0;
        wl_int32 count = WLcJSON_GetArraySize(friends_obj);
        for (index = 0; index < count; index++) {
            WLcJSON* friend_obj = WLcJSON_GetArrayItem(friends_obj, index);
            if (wl_null != friend_obj) {
                repo_friend* friendinfo = (repo_friend*)wl_malloc(sizeof(repo_friend));
                if (wl_null != friendinfo) {
                    WLcJSON* obj = wl_null;
                    memset(friendinfo, 0, sizeof(repo_friend));
                    obj = WLcJSON_GetObjectItem(friend_obj, "uid");
                    if (obj) {
                        friendinfo->friend.userid = (wl_uint32)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(friend_obj, "nu");
                    if (obj && WLcJSON_IsString(obj)) {
                        strncpy(
                            friendinfo->friend.number, WLcJSON_GetStringValue(obj), sizeof(friendinfo->friend.number));
                        friendinfo->friend.number[sizeof(friendinfo->friend.number) - 1] = '\0';
                    }
                    obj = WLcJSON_GetObjectItem(friend_obj, "nk");
                    if (obj && WLcJSON_IsString(obj)) {
                        strncpy(friendinfo->friend.nick, WLcJSON_GetStringValue(obj), sizeof(friendinfo->friend.nick));
                        friendinfo->friend.nick[sizeof(friendinfo->friend.nick) - 1] = '\0';
                    }
                    obj = WLcJSON_GetObjectItem(friend_obj, "sex");
                    if (obj) {
                        friendinfo->friend.sex = (wl_uint16)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(friend_obj, "sds");
                    if (obj) {
                        friendinfo->friend.shieldstatus = (wl_uint16)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(friend_obj, "tts");
                    if (obj) {
                        friendinfo->friend.tts = (wl_uint16)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(friend_obj, "ls");
                    if (obj) {
                        friendinfo->friend.locationshare = (wl_uint16)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(friend_obj, "type");
                    if (obj) {
                        friendinfo->type = (wl_uint16)WLcJSON_GetNumberValue(obj);
                    }
                    if (0 > wl_list_add(&s_repo_global.friend_list, friendinfo, -1)) {
                        wl_free(friendinfo);
                        break;
                    }
                }
            }
        }
    }
    WLcJSON_Delete(store);
    return 0;
}

wl_int32 repo_friend_save(void) {
    wl_list_iterator iterator;
    repo_friend*     friendinfo  = wl_null;
    WLcJSON*         friends_obj = wl_null;
    WLcJSON*         store       = WLcJSON_CreateObject();
    if (wl_null == store) {
        WLLOGW("create object fail");
        return -1;
    }
    friends_obj = WLcJSON_CreateArray();
    if (wl_null == friends_obj) {
        WLLOGW("create object fail");
        WLcJSON_Delete(store);
        return -1;
    }

    friendinfo = (repo_friend*)wl_list_get_first(&s_repo_global.friend_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        WLcJSON* friend_obj = WLcJSON_CreateObject();
        if (wl_null != friend_obj) {
            WLcJSON_AddNumberToObject(friend_obj, "uid", friendinfo->friend.userid);
            WLcJSON_AddStringToObject(friend_obj, "nu", friendinfo->friend.number);
            WLcJSON_AddStringToObject(friend_obj, "nk", friendinfo->friend.nick);
            WLcJSON_AddNumberToObject(friend_obj, "sex", friendinfo->friend.sex);
            WLcJSON_AddNumberToObject(friend_obj, "sds", friendinfo->friend.shieldstatus);
            WLcJSON_AddNumberToObject(friend_obj, "tts", friendinfo->friend.tts);
            WLcJSON_AddNumberToObject(friend_obj, "ls", friendinfo->friend.locationshare);
            WLcJSON_AddNumberToObject(friend_obj, "type", friendinfo->type);
            WLcJSON_AddItemToArray(friends_obj, friend_obj);
        }
        friendinfo = (repo_friend*)wl_list_get_next(&iterator);
    }
    WLcJSON_AddNumberToObject(store, "updated", s_repo_global.friend_updated);
    WLcJSON_AddNumberToObject(store, "version", s_repo_global.corp_address_version);
    if (!WLcJSON_AddItemToObject(store, "friends", friends_obj)) {
        WLLOGW("add item to obj fail");
        WLcJSON_Delete(store);
        WLcJSON_Delete(friends_obj);
        return -1;
    }
    repo_save(REPO_MSG_SAVE_STORE_FRIEND, store);
    WLcJSON_Delete(store);
    // 添加个人管理员会话
    repo_add_single_manager_session();
    // 刷新好友会话
    repo_refresh_friend_sessions();
    return 0;
}

wl_int32 repo_set_friend_updated(wl_uint64 updated) {
    if (updated != s_repo_global.friend_updated) {
        s_repo_global.friend_updated = updated;
        return repo_friend_save();
    }
    return 0;
}

wl_uint64 repo_get_friend_updated(void) {
    return s_repo_global.friend_updated;
}

wl_int32 repo_delete_friend(wl_uint32 userid) {
    wl_list_iterator iterator;
    repo_friend*     friendinfo = (repo_friend*)wl_list_get_first(&s_repo_global.friend_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (friendinfo->friend.userid == userid) {
            wl_free(friendinfo);
            wl_list_iterator_remove(&iterator);
            break;
        }
        friendinfo = (repo_friend*)wl_list_get_next(&iterator);
    }
    return repo_friend_save();
}

wl_int32 repo_set_friends(wl_uint64 updated, repo_friend* friends, wl_uint32 count) {
    wl_uint32 index = 0;
    // 好友未变更
    if (s_repo_global.friend_updated >= updated) {
        return 0;
    }
    s_repo_global.friend_updated = updated;
    wl_list_free(&s_repo_global.friend_list);
    for (index = 0; index < count && index < REPO_FRIEND_MAX; index++) {
        repo_friend* friendinfo = (repo_friend*)wl_malloc(sizeof(repo_friend));
        if (wl_null != friendinfo) {
            memcpy(friendinfo, &friends[index], sizeof(repo_friend));
            if (0 > wl_list_add(&s_repo_global.friend_list, friendinfo, -1)) {
                wl_free(friendinfo);
                return -1;
            }
        } else {
            s_repo_global.friend_updated = 0;
        }
    }
    return repo_friend_save();
}

void repo_refresh_friend_sessions(void) {
    wl_list_iterator iterator;
    repo_friend* friend = (repo_friend*)wl_list_get_first(&s_repo_global.friend_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (!s_repo_global.config.contact_session_hold) {
            repo_update_friend_session(friend);
        } else {
            repo_session session;
            repo_add_friend_session(friend);
            if (0 != repo_get_default_session(&session)) {
                repo_set_default_session(friend->friend.userid, WLSDK_SESSION_TYPE_SINGLE);
            }
        }
        friend = (repo_friend*)wl_list_get_next(&iterator);
    }
}

wl_list* repo_get_friend_list(void) {
    return &s_repo_global.friend_list;
}

void repo_clear_friend_list(void) {
    wl_list_free(&s_repo_global.friend_list);
    wl_list_init(&s_repo_global.friend_list);
    wl_ext_fs_delete(REPO_FRIEND_STORE_NAME);
}

wl_int32 repo_get_friend(wl_uint32 userid, repo_friend* friend) {
    wl_list_iterator iterator;
    repo_friend*     friendinfo = (repo_friend*)wl_list_get_first(&s_repo_global.friend_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (friendinfo->friend.userid == userid) {
            memcpy(friend, friendinfo, sizeof(repo_friend));
            return 0;
        }
        friendinfo = (repo_friend*)wl_list_get_next(&iterator);
    }
    return -1;
}

wl_int32 repo_session_load(void) {
    WLcJSON* sessions_obj            = wl_null;
    WLcJSON* store                   = wl_null;
    WLcJSON* default_sessionid_obj   = wl_null;
    WLcJSON* default_sessiontype_obj = wl_null;
    wl_list_free(&s_repo_global.session_list);
    wl_list_init(&s_repo_global.session_list);
    store = repo_store_read_json(REPO_SESSION_STORE_NAME);
    if (wl_null == store) {
        WLLOGW("repo_store_read fail");
        return -1;
    }
    default_sessionid_obj   = WLcJSON_GetObjectItem(store, "default_sessionid");
    default_sessiontype_obj = WLcJSON_GetObjectItem(store, "default_sessiontype");
    if (wl_null != default_sessionid_obj && wl_null != default_sessiontype_obj) {
        s_repo_global.default_sessionid   = (wl_uint64)WLcJSON_GetNumberValue(default_sessionid_obj);
        s_repo_global.default_sessiontype = (wl_uint64)WLcJSON_GetNumberValue(default_sessiontype_obj);
    }
    sessions_obj = WLcJSON_GetObjectItem(store, "sessions");
    if (wl_null != sessions_obj) {
        wl_int32 index = 0;
        wl_int32 count = WLcJSON_GetArraySize(sessions_obj);
        for (index = 0; index < count; index++) {
            WLcJSON* session_obj = WLcJSON_GetArrayItem(sessions_obj, index);
            if (wl_null != session_obj) {
                repo_session* sessioninfo = (repo_session*)wl_malloc(sizeof(repo_session));
                if (wl_null != sessioninfo) {
                    WLcJSON* obj = wl_null;
                    memset(sessioninfo, 0, sizeof(repo_session));
                    obj = WLcJSON_GetObjectItem(session_obj, "id");
                    if (obj) {
                        sessioninfo->session.sessionid = (wl_uint64)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(session_obj, "type");
                    if (obj) {
                        sessioninfo->session.sessiontype = (wl_uint16)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(session_obj, "name");
                    if (obj && WLcJSON_IsString(obj)) {
                        strncpy(
                            sessioninfo->session.name, WLcJSON_GetStringValue(obj), sizeof(sessioninfo->session.name));
                        sessioninfo->session.name[sizeof(sessioninfo->session.name) - 1] = '\0';
                    }
                    obj = WLcJSON_GetObjectItem(session_obj, "latest_active");
                    if (obj) {
                        sessioninfo->latest_active = (wl_time)WLcJSON_GetNumberValue(obj);
                    }
                    sessioninfo->sync_timestamp = 1;  // 会话每次开机默认需要同步
                    if (0 > wl_list_add(&s_repo_global.session_list, sessioninfo, -1)) {
                        wl_free(sessioninfo);
                        break;
                    }
                }
            }
        }
    }
    WLcJSON_Delete(store);
    return 0;
}

wl_int32 repo_session_save(void) {
    wl_list_iterator iterator;
    repo_session*    sessioninfo  = wl_null;
    WLcJSON*         sessions_obj = wl_null;
    WLcJSON*         store        = WLcJSON_CreateObject();
    if (wl_null == store) {
        WLLOGW("create object fail");
        return -1;
    }
    WLcJSON_AddNumberToObject(store, "default_sessionid", s_repo_global.default_sessionid);
    WLcJSON_AddNumberToObject(store, "default_sessiontype", s_repo_global.default_sessiontype);
    sessions_obj = WLcJSON_CreateArray();
    if (wl_null == sessions_obj) {
        WLLOGW("create object fail");
        WLcJSON_Delete(store);
        return -1;
    }
    sessioninfo = (repo_session*)wl_list_get_first(&s_repo_global.session_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        WLcJSON* session_obj = WLcJSON_CreateObject();
        if (wl_null != session_obj) {
            WLcJSON_AddNumberToObject(session_obj, "id", sessioninfo->session.sessionid);
            WLcJSON_AddNumberToObject(session_obj, "type", sessioninfo->session.sessiontype);
            WLcJSON_AddStringToObject(session_obj, "name", sessioninfo->session.name);
            WLcJSON_AddNumberToObject(session_obj, "latest_active", sessioninfo->latest_active);

            WLcJSON_AddItemToArray(sessions_obj, session_obj);
        }
        sessioninfo = (repo_session*)wl_list_get_next(&iterator);
    }
    if (!WLcJSON_AddItemToObject(store, "sessions", sessions_obj)) {
        WLLOGW("add item to obj fail");
        WLcJSON_Delete(store);
        WLcJSON_Delete(sessions_obj);
        return -1;
    }
    repo_save(REPO_MSG_SAVE_STORE_SESSION, store);
    WLcJSON_Delete(store);
    return 0;
}

wl_list* repo_get_session_list(void) {
    return &s_repo_global.session_list;
}

void repo_clear_session_list(void) {
    wl_list_free(&s_repo_global.session_list);
    wl_list_init(&s_repo_global.session_list);
    wl_ext_fs_delete(REPO_SESSION_STORE_NAME);
}

static wl_int32 __add_session(repo_session* session, wl_int32 pos) {
    wl_list_iterator iterator;
    repo_session*    sessioninfo = (repo_session*)wl_list_get_first(&s_repo_global.session_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        // 已存在session
        if (sessioninfo->session.sessionid == session->session.sessionid
            && sessioninfo->session.sessiontype == session->session.sessiontype) {
            wl_free(sessioninfo);
            sessioninfo = wl_list_iterator_remove(&iterator);
            break;
        }
        sessioninfo = (repo_session*)wl_list_get_next(&iterator);
    }
    sessioninfo = (repo_session*)wl_malloc(sizeof(repo_session));
    if (wl_null == sessioninfo) {
        WLLOGW("malloc fail");
        return -1;
    }
    memcpy(sessioninfo, session, sizeof(repo_session));
    if (-1 == wl_list_add(&s_repo_global.session_list, sessioninfo, pos)) {
        WLLOGW("wl_list_add fail");
        wl_free(sessioninfo);
        return -1;
    }
    return 0;
}

wl_int32 repo_add_friend_session(repo_friend* friend) {
    repo_session session;
    if (0 == repo_get_session(friend->friend.userid, WLSDK_SESSION_TYPE_SINGLE, &session)) {
        strncpy(session.session.name, friend->friend.nick, sizeof(session.session.name));
        session.session.name[sizeof(session.session.name) - 1] = '\0';
        session.sync_timestamp                                 = 0;
        return repo_update_session(&session);
    } else {
        memset(&session, 0, sizeof(repo_session));
        session.session.sessionid    = friend->friend.userid;
        session.session.sessiontype  = WLSDK_SESSION_TYPE_SINGLE;
        session.session.online_count = 1;
        strncpy(session.session.name, friend->friend.nick, sizeof(session.session.name));
        session.session.name[sizeof(session.session.name) - 1] = '\0';
        session.sync_timestamp                                 = 0;
        session.latest_active                                  = repo_get_server_timestamp();
        return repo_add_session(&session, -1);
    }
}

wl_int32 repo_update_friend_session(repo_friend* friend) {
    repo_session session;
    if (0 == repo_get_session(friend->friend.userid, WLSDK_SESSION_TYPE_SINGLE, &session)) {
        strncpy(session.session.name, friend->friend.nick, sizeof(session.session.name));
        session.session.name[sizeof(session.session.name) - 1] = '\0';
        session.sync_timestamp                                 = 0;
        return repo_update_session(&session);
    }
    return 0;
}

wl_int32 repo_add_group_session(repo_group* group) {
    repo_session session;
    if (0 == repo_get_session(group->group.groupid, WLSDK_SESSION_TYPE_GROUP, &session)) {
        strncpy(session.session.name, group->group.group_name, sizeof(session.session.name));
        session.session.name[sizeof(session.session.name) - 1] = '\0';
        session.sync_timestamp                                 = 0;
        return repo_update_session(&session);
    } else {
        memset(&session, 0, sizeof(repo_session));
        session.session.sessionid    = group->group.groupid;
        session.session.sessiontype  = WLSDK_SESSION_TYPE_GROUP;
        session.session.online_count = 1;
        strncpy(session.session.name, group->group.group_name, sizeof(session.session.name));
        session.session.name[sizeof(session.session.name) - 1] = '\0';
        session.sync_timestamp                                 = 0;
        session.latest_active                                  = repo_get_server_timestamp();
        return repo_add_session(&session, -1);
    }
}

wl_int32 repo_update_group_session(repo_group* group) {
    repo_session session;
    if (0 == repo_get_session(group->group.groupid, WLSDK_SESSION_TYPE_GROUP, &session)) {
        strncpy(session.session.name, group->group.group_name, sizeof(session.session.name));
        session.session.name[sizeof(session.session.name) - 1] = '\0';
        session.sync_timestamp                                 = 0;
        return repo_update_session(&session);
    }
    return 0;
}

void repo_refresh_group_sessions(void) {
    wl_list_iterator iterator;
    repo_group*      group = (repo_group*)wl_list_get_first(&s_repo_global.group_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        repo_session session;
        if (!s_repo_global.config.contact_session_hold) {
            if (WLSDK_GROUP_NORMAL == group->group.group_type) {
                repo_add_group_session(group);
                if (0 != repo_get_default_session(&session)) {
                    repo_set_default_session(group->group.groupid, WLSDK_SESSION_TYPE_GROUP);
                }
            } else {
                repo_update_group_session(group);
            }
        } else {
            repo_add_group_session(group);
            if (0 != repo_get_default_session(&session)) {
                repo_set_default_session(group->group.groupid, WLSDK_SESSION_TYPE_GROUP);
            }
        }
        group = (repo_group*)wl_list_get_next(&iterator);
    }
}

void repo_add_single_manager_session(void) {
#ifndef MANAGER_SESSION_KEEP
    // 没有会话时,绑定者作为默认会话
    if (0 == wl_list_size(&s_repo_global.session_list)) {
#endif
        repo_friend friend;
        if (0 == repo_get_friend(s_repo_global.config.managerid, &friend)) {
            repo_add_friend_session(&friend);
        }
#ifndef MANAGER_SESSION_KEEP
    }
#endif
}

wl_bool repo_remove_invalid_session(void) {
    wl_list_iterator iterator;
    wl_bool          is_remove    = wl_false;
    wl_time          current_time = repo_get_server_timestamp();
    repo_session*    sessioninfo  = (repo_session*)wl_list_get_first(&s_repo_global.session_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (WLSDK_SESSION_TYPE_GROUP == sessioninfo->session.sessiontype) {
            repo_group group;
            if (0 == repo_get_group(sessioninfo->session.sessionid, &group)) {
                if (!s_repo_global.config.contact_session_hold) {
                    // 固定群会话不清理
                    if (WLSDK_GROUP_NORMAL == group.group.group_type) {
                        sessioninfo = (repo_session*)wl_list_get_next(&iterator);
                        continue;
                    }
                }
                // 通讯录会话保持,不删除群与好友会话
                else {
                    sessioninfo = (repo_session*)wl_list_get_next(&iterator);
                    continue;
                }
            }
        }
        if (WLSDK_SESSION_TYPE_SINGLE == sessioninfo->session.sessiontype) {
            repo_friend friend;
            if (0 == repo_get_friend(sessioninfo->session.sessionid, &friend)) {
                // 通讯录会话保持,不删除群与好友会话
                if (s_repo_global.config.contact_session_hold) {
                    sessioninfo = (repo_session*)wl_list_get_next(&iterator);
                    continue;
                }
            }
        }
#ifdef MANAGER_SESSION_KEEP
        // 管理员不清理
        if (WLSDK_SESSION_TYPE_SINGLE == sessioninfo->session.sessiontype
            && sessioninfo->session.sessionid == s_repo_global.config.managerid) {
            sessioninfo = (repo_session*)wl_list_get_next(&iterator);
            continue;
        }
#endif
        if (current_time > sessioninfo->latest_active + s_repo_global.config.tmp_session_keeptime) {
            wl_free(sessioninfo);
            sessioninfo = wl_list_iterator_remove(&iterator);
            is_remove   = wl_true;
            continue;
        }
        sessioninfo = (repo_session*)wl_list_get_next(&iterator);
    }
    // 添加个人管理员会话
    repo_add_single_manager_session();
    if (is_remove) {
        repo_session_save();
    }
    return is_remove;
}

wl_int32 repo_add_session(repo_session* session, wl_int32 pos) {
    if (0 == __add_session(session, pos)) {
        return repo_session_save();
    } else {
        return -1;
    }
}

wl_int32 repo_del_session(wl_uint64 sessionid, wl_uint32 sessiontype) {
    wl_list_iterator iterator;
    repo_session*    sessioninfo = (repo_session*)wl_list_get_first(&s_repo_global.session_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (sessioninfo->session.sessionid == sessionid && sessioninfo->session.sessiontype == sessiontype) {
            wl_free(sessioninfo);
            wl_list_iterator_remove(&iterator);
            return repo_session_save();
        }
        sessioninfo = (repo_session*)wl_list_get_next(&iterator);
    }
    return 0;
}

wl_int32 repo_close_session(wl_uint64 sessionid, wl_uint32 sessiontype) {
    if (sessionid == s_repo_global.active_sessionid && sessiontype == s_repo_global.active_sessiontype) {
        s_repo_global.active_sessionid   = s_repo_global.default_sessionid;
        s_repo_global.active_sessiontype = s_repo_global.default_sessiontype;
    }
    return 0;
}

wl_int32 repo_get_session_prority(wl_uint64 sessionid, wl_uint32 sessiontype) {
    if (0 == sessionid) {
        return REPO_INVALID_SESSION_PRORITY;
    }
    if (s_repo_global.default_sessionid == sessionid && s_repo_global.default_sessiontype == sessiontype) {
        // 当前会话不保持,默认会话优先级
        if (!s_repo_global.config.burst_session_hold) {
            return REPO_DEFAULT_SESSION_PRORITY;
        }
    }
    if (s_repo_global.active_sessionid == sessionid && s_repo_global.active_sessiontype == sessiontype) {
        wl_int32 prority = REPO_ACTIVE_SESSION_PRORITY;
        if (WLSDK_SESSION_TYPE_SINGLE == sessiontype) {
            prority = REPO_DISPATCH_ACTIVE_SESSION_PRORITY;
        } else {
            if (WLSDK_SESSION_TYPE_GROUP == sessiontype) {
                repo_group group;
                if (0 == repo_get_group(sessionid, &group)) {
                    if (group.group.group_type == WLSDK_GROUP_TMP) {
                        prority = REPO_DISPATCH_ACTIVE_SESSION_PRORITY;
                    }
                }
            }
        }
        // 活跃会话话权释放后,优先级降级,防止只有多个同优先级会话时,无法切换会话
        if (repo_is_session_burst_release(sessionid, sessiontype)) {
            prority -= REPO_TMP_SESSION_PRORITY_COUNT;
        }
        return prority;
    } else {
        wl_int32 prority = REPO_QUEUE_SESSION_PRORITY;
        if (WLSDK_SESSION_TYPE_SINGLE == sessiontype) {
            prority = REPO_DISPATCH_QUEUE_SESSION_PRORITY;
        } else {
            if (WLSDK_SESSION_TYPE_GROUP == sessiontype) {
                repo_group group;
                if (0 == repo_get_group(sessionid, &group)) {
                    if (group.group.group_type == WLSDK_GROUP_TMP) {
                        prority = REPO_DISPATCH_QUEUE_SESSION_PRORITY;
                    }
                }
            }
        }
        return prority;
    }
}

wl_int32 repo_get_session(wl_uint64 sessionid, wl_uint32 sessiontype, repo_session* session) {
    wl_list_iterator iterator;
    repo_session*    sessioninfo = (repo_session*)wl_list_get_first(&s_repo_global.session_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (sessioninfo->session.sessionid == sessionid && sessioninfo->session.sessiontype == sessiontype) {
            memcpy(session, sessioninfo, sizeof(repo_session));
            if (sessionid == s_repo_global.default_sessionid && sessiontype == s_repo_global.default_sessiontype) {
                session->session.isdefault = wl_true;
            } else {
                session->session.isdefault = wl_false;
            }
            return 0;
        }
        sessioninfo = (repo_session*)wl_list_get_next(&iterator);
    }
    return -1;
}

wl_int32 repo_get_session_by_pos(wl_uint32 pos, repo_session* session) {
    repo_session* sessioninfo = (repo_session*)wl_list_get(&s_repo_global.session_list, pos);
    if (sessioninfo) {
        memcpy(session, sessioninfo, sizeof(repo_session));
        return 0;
    }
    return -1;
}

wl_int32 repo_update_session(repo_session* session) {
    wl_list_iterator iterator;
    repo_session*    sessioninfo = (repo_session*)wl_list_get_first(&s_repo_global.session_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (sessioninfo->session.sessionid == session->session.sessionid
            && sessioninfo->session.sessiontype == session->session.sessiontype) {
            memcpy(sessioninfo, session, sizeof(repo_session));
            repo_get_session(session->session.sessionid, session->session.sessiontype, session);
            return repo_session_save();
        }
        sessioninfo = (repo_session*)wl_list_get_next(&iterator);
    }
    return -1;
}

wl_int32 repo_update_session_name(wl_uint64 sessionid, wl_uint32 sessiontype, wl_char* name) {
    wl_list_iterator iterator;
    repo_session*    sessioninfo = (repo_session*)wl_list_get_first(&s_repo_global.session_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (sessioninfo->session.sessionid == sessionid && sessioninfo->session.sessiontype == sessiontype) {
            strncpy(sessioninfo->session.name, name, sizeof(sessioninfo->session.name));
            sessioninfo->session.name[sizeof(sessioninfo->session.name) - 1] = '\0';
            repo_session_save();
        }
        sessioninfo = (repo_session*)wl_list_get_next(&iterator);
    }
    return 0;
}

wl_int32 repo_update_session_sync_timestamp(wl_uint64 sessionid, wl_uint32 sessiontype, wl_time timestamp) {
    wl_list_iterator iterator;
    repo_session*    sessioninfo = (repo_session*)wl_list_get_first(&s_repo_global.session_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (sessioninfo->session.sessionid == sessionid && sessioninfo->session.sessiontype == sessiontype) {
            sessioninfo->sync_timestamp = timestamp;
            repo_session_save();
        }
        sessioninfo = (repo_session*)wl_list_get_next(&iterator);
    }
    return 0;
}

wl_int32 repo_update_session_online(wl_uint64 sessionid, wl_uint32 sessiontype, wl_uint32 online_count) {
    wl_list_iterator iterator;
    repo_session*    sessioninfo = (repo_session*)wl_list_get_first(&s_repo_global.session_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (sessioninfo->session.sessionid == sessionid && sessioninfo->session.sessiontype == sessiontype) {
            sessioninfo->session.online_count = online_count;
        }
        sessioninfo = (repo_session*)wl_list_get_next(&iterator);
    }
    return 0;
}

wl_bool repo_is_normal_group_session(wl_uint64 sessionid, wl_uint32 sessiontype) {
    if (WLSDK_SESSION_TYPE_GROUP == sessiontype) {
        repo_group group;
        if (0 == repo_get_group(sessionid, &group)) {
            if (WLSDK_GROUP_NORMAL == group.group.group_type) {
                return wl_true;
            }
        }
    }
    return wl_false;
}

wl_int32 repo_refresh_session_burst(wl_uint64 sessionid, wl_uint32 sessiontype) {
    wl_list_iterator iterator;
    repo_session*    sessioninfo = (repo_session*)wl_list_get_first(&s_repo_global.session_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (sessioninfo->session.sessionid == sessionid && sessioninfo->session.sessiontype == sessiontype) {
            sessioninfo->latest_burst = repo_get_server_timestamp();
            // 间隔1分钟保存一次
            if (repo_get_server_timestamp() > sessioninfo->latest_active + 60) {
                sessioninfo->latest_active = repo_get_server_timestamp();
                repo_session_save();
            }
            return 0;
        }
        sessioninfo = (repo_session*)wl_list_get_next(&iterator);
    }
    return 0;
}

wl_int32 repo_set_active_session(repo_session* session) {
    s_repo_global.active_sessionid   = session->session.sessionid;
    s_repo_global.active_sessiontype = session->session.sessiontype;
    session->latest_burst            = repo_get_server_timestamp();
    // 当前会话保持不自动回退
    if (s_repo_global.config.burst_session_hold) {
        if (!repo_is_session_default(session->session.sessionid, session->session.sessiontype)) {
            repo_set_default_session(session->session.sessionid, session->session.sessiontype);
        }
    }
    return repo_add_session(session, 0);
}

wl_int32 repo_get_active_session(repo_session* session) {
    if (wl_list_size(&s_repo_global.session_list) <= 0) {
        return -1;
    }
    // active会话存在
    if (0 == repo_get_session(s_repo_global.active_sessionid, s_repo_global.active_sessiontype, session)) {
        // 话权活跃
        if (!repo_is_session_burst_release(session->session.sessionid, session->session.sessiontype)) {
            return 0;
        }
    }
    // 没有合格会话,优先default会话
    if (0 == repo_get_session(s_repo_global.default_sessionid, s_repo_global.default_sessiontype, session)) {
        s_repo_global.active_sessionid   = s_repo_global.default_sessionid;
        s_repo_global.active_sessiontype = s_repo_global.default_sessiontype;
        return 0;
    }
    // 没有默认会话,选优先级最低的会话
    {
        wl_list_iterator iterator;
        repo_session*    sessioninfo     = (repo_session*)wl_list_get_first(&s_repo_global.session_list, &iterator);
        wl_uint32        session_prority = REPO_DISPATCH_ACTIVE_SESSION_PRORITY;
        while (!wl_list_iterator_is_end(&iterator)) {
            wl_uint32 prority =
                repo_get_session_prority(sessioninfo->session.sessionid, sessioninfo->session.sessiontype);
            if (prority <= session_prority) {
                memcpy(session, sessioninfo, sizeof(repo_session));
                s_repo_global.active_sessionid   = sessioninfo->session.sessionid;
                s_repo_global.active_sessiontype = sessioninfo->session.sessiontype;
                if (WLSDK_SESSION_TYPE_GROUP == sessioninfo->session.sessiontype) {
                    session->session.isdefault = wl_false;
                } else {
                    session->session.isdefault = wl_false;
                }
                session_prority = prority;
                if (s_repo_global.config.burst_session_hold) {
                    repo_set_default_session(s_repo_global.active_sessionid, s_repo_global.active_sessiontype);
                }
            }
            sessioninfo = (repo_session*)wl_list_get_next(&iterator);
        }
    }
    return 0;
}

void repo_set_tmp_session_keeptime(wl_uint32 duration) {
    s_repo_global.config.tmp_session_keeptime = duration;
    repo_config_save();
}

wl_int32 repo_refresh_default_session(void) {
    // 当前会话不保持,默认会话为群会话
    if (!s_repo_global.config.burst_session_hold) {
        // APP配置默认群
        if (0 != s_repo_global.config.default_sessionid
            && WLSDK_SESSION_TYPE_GROUP == s_repo_global.config.default_sessiontype) {
            if (repo_is_normal_group(s_repo_global.default_sessionid)) {
                s_repo_global.default_sessionid   = s_repo_global.config.default_sessionid;
                s_repo_global.default_sessiontype = s_repo_global.config.default_sessiontype;
                repo_session_save();
                return 0;
            }
        }
    }
    return 0;
}

wl_int32 repo_set_default_session(wl_uint64 sessionid, wl_uint16 sessiontype) {
    // 当前会话不保持,默认会话为群会话
    if (!s_repo_global.config.burst_session_hold) {
        // APP配置默认群
        if (WLSDK_SESSION_TYPE_GROUP == sessiontype && repo_is_normal_group(sessionid)) {
            s_repo_global.default_sessionid   = sessionid;
            s_repo_global.default_sessiontype = sessiontype;
            repo_session_save();
            return 0;
        }
    } else {
        s_repo_global.default_sessionid   = sessionid;
        s_repo_global.default_sessiontype = sessiontype;
        repo_session_save();
    }
    return 0;
}

wl_int32 repo_get_default_session(repo_session* session) {
    if (0 == repo_get_session(s_repo_global.default_sessionid, s_repo_global.default_sessiontype, session)) {
        return 0;
    }
    return -1;
}

wl_bool repo_is_session_default(wl_uint64 sessionid, wl_uint16 sessiontype) {
    if (s_repo_global.default_sessionid == sessionid && s_repo_global.default_sessiontype == sessiontype) {
        return wl_true;
    }
    return wl_false;
}

wl_bool repo_is_session_active(wl_uint64 sessionid, wl_uint16 sessiontype) {
    if (s_repo_global.active_sessionid == sessionid && s_repo_global.active_sessiontype == sessiontype) {
        return wl_true;
    }
    return wl_false;
}

wl_bool repo_is_session_burst_release(wl_uint64 sessionid, wl_uint16 sessiontype) {
    repo_session session;
    if (0 == repo_get_session(sessionid, sessiontype, &session)) {
        // 话权释放
        if (repo_get_server_timestamp() > (session.latest_burst + s_repo_global.config.active_session_lifetime)) {
            return wl_true;
        }
        return wl_false;
    }
    return wl_true;
}

wl_uint32 repo_get_session_burst_time_remaining(wl_uint64 sessionid, wl_uint16 sessiontype) {
    repo_session session;
    if (0 == repo_get_session(sessionid, sessiontype, &session)) {
        // 话权释放
        if (repo_get_server_timestamp() <= (session.latest_burst + s_repo_global.config.active_session_lifetime)) {
            return (session.latest_burst + s_repo_global.config.active_session_lifetime) - repo_get_server_timestamp();
        }
    }
    return 0;
}

wl_int32 repo_set_attach_group_session(wl_uint64 sessionid) {
    s_repo_global.attach_sessionid = sessionid;
    return 0;
}
wl_bool repo_is_attach_group_session(wl_uint64 sessionid) {
    return s_repo_global.attach_sessionid == sessionid;
}

wl_int32 repo_add_burst_playback(repo_playback* playback) {
    if (wl_null != playback) {
        // 长度要减去audio定义长度
        wl_int32 size = sizeof(repo_playback) + playback->audiolen - sizeof(playback->audio);
        wl_core_send_message(WL_MODULE_REPO, REPO_MSG_SAVE_STORE_PLAYBACK, playback, size);
    }
    return 0;
}

repo_playback* repo_get_burst_playback(wl_uint32 idx) {
    wl_char        path[MAX_NAME_LEN];
    wl_uint32      buffer_size = 0;
    wl_size        readlen     = 0;
    wl_int32       framecount  = 0;
    wl_ext_fs_handle fhandle   = WL_EXT_INVALID_FS;
    repo_playback* playback    = wl_null;
    wl_ssize       ret         = 0;
    wl_uint32      pos         = 0;
    idx                        = idx % MAX_PLAYBACK_NUM;

    sprintf(path, "%d.ptt", idx);
    fhandle = wl_ext_fs_open(path, WL_EXT_RDONLY);
    // 文件不存在,新用户
    if (WL_EXT_INVALID_FS == fhandle) {
        WLLOGW("open repo store fail");
        return wl_null;
    }
    buffer_size = wl_ext_fs_size_by_handle(fhandle);
    if (buffer_size <= sizeof(repo_playback)) {
        WLLOGW("path %s size %d", path, buffer_size);
        wl_ext_fs_close(fhandle);
        return wl_null;
    }
    if (WL_MEMORY_SIZE_LIMIT < buffer_size) {
        WLLOGW("buffer size %d more than fresh size 1/3", buffer_size);
        wl_ext_fs_close(fhandle);
        return wl_null;
    }
    playback = (repo_playback*)wl_malloc(buffer_size);
    if (wl_null == playback) {
        WLLOGW("buffer_size %d", buffer_size);
        wl_ext_fs_close(fhandle);
        return wl_null;
    }
    // 先读playback头(audio不读取)
    ret = wl_ext_fs_read(fhandle, playback, (sizeof(repo_playback) - sizeof(playback->audio)));
    if ((sizeof(repo_playback) - sizeof(playback->audio)) != ret) {
        WLLOGW("ret %d", ret);
        wl_ext_fs_close(fhandle);
        wl_free(playback);
        return wl_null;
    }
    // audio不能重复计算
    if (buffer_size != ((sizeof(repo_playback) - sizeof(playback->audio)) + playback->audiolen)) {
        WLLOGW("buffer_size %d playback size %d",
               buffer_size,
               ((sizeof(repo_playback) - sizeof(playback->audio)) + playback->audiolen));
        wl_ext_fs_close(fhandle);
        wl_free(playback);
        return wl_null;
    }

    readlen = buffer_size > MAX_BUFFER_LEN ? MAX_BUFFER_LEN : buffer_size;
    while (0 < (ret = wl_ext_fs_read(fhandle, playback->audio + pos, readlen))) {
        pos += ret;
    }
    if (0 > ret) {
        WLLOGW("store data len %d invalid", ret);
        wl_ext_fs_close(fhandle);
        wl_free(playback);
        return wl_null;
    }
    wl_ext_fs_close(fhandle);
    if (pos != playback->audiolen) {
        WLLOGW("store framecount %d frames %d data len %d audio len %d",
               framecount,
               playback->framecount,
               pos,
               playback->audiolen);
        wl_free(playback);
        return wl_null;
    }
    return playback;
}

wl_int32 _add_dns(wl_char* host, wl_char* ip) {
    // 每个host只保存一个ip
    repo_dns*        dnsinfo = wl_null;
    wl_list_iterator iterator;
    dnsinfo = (repo_dns*)wl_list_get_first(&s_repo_global.dns_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        WLLOGW("_add_dns host %s ip %s", dnsinfo->host, dnsinfo->ip);
        if (0 == strncmp(dnsinfo->host, host, sizeof(dnsinfo->host))) {
            strncpy(dnsinfo->ip, ip, sizeof(dnsinfo->ip));
            dnsinfo->ip[sizeof(dnsinfo->ip) - 1] = '\0';
            break;
        }
        dnsinfo = (repo_dns*)wl_list_get_next(&iterator);
    }
    // 新host
    if (wl_null == dnsinfo) {
        // DNS数量超过限制,替换第一个DNS
        if (wl_list_size(&s_repo_global.dns_list) >= REPO_DNS_MAX) {
            repo_dns* dns = wl_list_get(&s_repo_global.dns_list, 0);
            if (wl_null != dns) {
                memset(dns, 0, sizeof(repo_dns));
                strncpy(dns->host, host, sizeof(dns->host));
                dns->host[sizeof(dns->host) - 1] = '\0';
                strncpy(dns->ip, ip, sizeof(dns->ip));
                dns->ip[sizeof(dns->ip) - 1] = '\0';
            }
        } else {
            repo_dns* dns = (repo_dns*)wl_malloc(sizeof(repo_dns));
            if (wl_null != dns) {
                memset(dns, 0, sizeof(repo_dns));
                strncpy(dns->host, host, sizeof(dns->host));
                dns->host[sizeof(dns->host) - 1] = '\0';
                strncpy(dns->ip, ip, sizeof(dns->ip));
                dns->ip[sizeof(dns->ip) - 1] = '\0';
                if (0 > wl_list_add(&s_repo_global.dns_list, dns, -1)) {
                    wl_free(dns);
                    return -1;
                }
            }
        }
    }
    return 0;
}

wl_int32 repo_dns_load(void) {
    WLcJSON* store    = wl_null;
    WLcJSON* dnss_obj = wl_null;
    wl_list_free(&s_repo_global.dns_list);
    wl_list_init(&s_repo_global.dns_list);

    store = repo_store_read_json(REPO_DNS_STORE_NAME);
    if (wl_null == store) {
        WLLOGW("repo_store_read fail");
        return -1;
    }
    dnss_obj = WLcJSON_GetObjectItem(store, "dnss");
    if (wl_null != dnss_obj) {
        wl_int32 index = 0;
        wl_int32 count = WLcJSON_GetArraySize(dnss_obj);
        for (index = 0; index < count; index++) {
            WLcJSON* dns_obj = WLcJSON_GetArrayItem(dnss_obj, index);
            if (wl_null != dns_obj) {
                WLcJSON*  obj = wl_null;
                repo_dns* dns = (repo_dns*)wl_malloc(sizeof(repo_dns));
                if (wl_null != dns) {
                    memset(dns, 0, sizeof(repo_dns));
                    obj = WLcJSON_GetObjectItem(dns_obj, "host");
                    if (obj && WLcJSON_IsString(obj)) {
                        strncpy(dns->host, WLcJSON_GetStringValue(obj), sizeof(dns->host));
                        dns->host[sizeof(dns->host) - 1] = '\0';
                    }
                    obj = WLcJSON_GetObjectItem(dns_obj, "ip");
                    if (obj && WLcJSON_IsString(obj)) {
                        strncpy(dns->ip, WLcJSON_GetStringValue(obj), sizeof(dns->ip));
                        dns->ip[sizeof(dns->ip) - 1] = '\0';
                    }
                    if (0 > wl_list_add(&s_repo_global.dns_list, dns, -1)) {
                        wl_free(dns);
                        break;
                    }
                }
            }
        }
    }
    WLcJSON_Delete(store);

    // 没有DNS配置,预制默认DNS
    if (wl_list_size(&s_repo_global.dns_list) <= 0) {
        _add_dns("webapi.weila.hk", "112.74.201.127");
        _add_dns("msg.weila.hk", "120.24.171.146");
        _add_dns("test.weila.hk", "119.23.131.222");
    }
    return 0;
}

wl_int32 repo_dns_save(void) {
    WLcJSON* store = WLcJSON_CreateObject();
    if (wl_null == store) {
        WLLOGW("create object fail");
        return -1;
    }

    // dns
    if (wl_list_size(&s_repo_global.dns_list) > 0) {
        WLcJSON*         dnss_obj = WLcJSON_CreateArray();
        repo_dns*        dns      = wl_null;
        wl_list_iterator iterator;
        if (wl_null == dnss_obj) {
            WLLOGW("create object fail");
            return -1;
        }
        dns = (repo_dns*)wl_list_get_first(&s_repo_global.dns_list, &iterator);
        while (!wl_list_iterator_is_end(&iterator)) {
            WLcJSON* dns_obj = WLcJSON_CreateObject();
            if (wl_null != dns_obj) {
                WLcJSON_AddStringToObject(dns_obj, "host", dns->host);
                WLcJSON_AddStringToObject(dns_obj, "ip", dns->ip);
                if (!WLcJSON_AddItemToArray(dnss_obj, dns_obj)) {
                    WLLOGW("add item to obj fail");
                    WLcJSON_Delete(dns_obj);
                    break;
                }
            }
            dns = (repo_dns*)wl_list_get_next(&iterator);
        }
        if (!WLcJSON_AddItemToObject(store, "dnss", dnss_obj)) {
            WLLOGW("add item to obj fail");
            WLcJSON_Delete(store);
            WLcJSON_Delete(dnss_obj);
            return -1;
        }
    }
    repo_save(REPO_MSG_SAVE_STORE_DNS, store);
    WLcJSON_Delete(store);
    return 0;
}

wl_int32 repo_add_dns(wl_char* host, wl_char* ip) {
    _add_dns(host, ip);
    return repo_dns_save();
}

wl_int32 repo_get_dns(wl_char* host, repo_dns* dns) {
    // 每个host只保存一个ip
    repo_dns*        dnsinfo = wl_null;
    wl_list_iterator iterator;
    dnsinfo = (repo_dns*)wl_list_get_first(&s_repo_global.dns_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        WLLOGW("repo_get_dns host %s host %s ip %s", host, dnsinfo->host, dnsinfo->ip);
        if (0 == strncmp(dnsinfo->host, host, sizeof(dnsinfo->host))) {
            memcpy(dns, dnsinfo, sizeof(repo_dns));
            return 0;
        }
        dnsinfo = (repo_dns*)wl_list_get_next(&iterator);
    }
    return -1;
}

wl_int32 repo_ttsindex_load(void) {
    WLcJSON* store         = wl_null;
    WLcJSON* ttsindexs_obj = wl_null;
    wl_list_free(&s_repo_global.ttsindex_list);
    wl_list_init(&s_repo_global.ttsindex_list);
    store = repo_store_read_json(REPO_TTSINDEX_STORE_NAME);
    if (wl_null == store) {
        WLLOGW("repo_store_read fail");
        return -1;
    }
    ttsindexs_obj = WLcJSON_GetObjectItem(store, "ttsindexs");
    if (wl_null != ttsindexs_obj) {
        wl_int32 index = 0;
        wl_int32 count = WLcJSON_GetArraySize(ttsindexs_obj);
        for (index = 0; index < count; index++) {
            WLcJSON* ttsindex_obj = WLcJSON_GetArrayItem(ttsindexs_obj, index);
            if (wl_null != ttsindex_obj) {
                repo_ttsindex* ttsindex = (repo_ttsindex*)wl_malloc(sizeof(repo_ttsindex));
                if (wl_null != ttsindex) {
                    WLcJSON* obj = wl_null;
                    memset(ttsindex, 0, sizeof(repo_ttsindex));
                    obj = WLcJSON_GetObjectItem(ttsindex_obj, "timestamp");
                    if (obj) {
                        ttsindex->timestamp = (wl_time)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(ttsindex_obj, "rshash");
                    if (obj) {
                        ttsindex->rshash = (wl_uint32)WLcJSON_GetNumberValue(obj);
                    }
                    obj = WLcJSON_GetObjectItem(ttsindex_obj, "jshash");
                    if (obj) {
                        ttsindex->jshash = (wl_uint32)WLcJSON_GetNumberValue(obj);
                    }

                    obj = WLcJSON_GetObjectItem(ttsindex_obj, "seq");
                    if (obj) {
                        ttsindex->seq = (wl_uint16)WLcJSON_GetNumberValue(obj);
                    }
                    if (0 > wl_list_add(&s_repo_global.ttsindex_list, ttsindex, -1)) {
                        wl_free(ttsindex);
                        break;
                    }
                }
            }
        }
    }
    WLcJSON_Delete(store);
    return 0;
}

wl_int32 repo_ttsindex_save(void) {
    wl_list_iterator iterator;
    repo_ttsindex*   ttsindex      = wl_null;
    WLcJSON*         ttsindexs_obj = wl_null;
    WLcJSON*         store         = WLcJSON_CreateObject();
    if (wl_null == store) {
        WLLOGW("create object fail");
        return -1;
    }
    ttsindexs_obj = WLcJSON_CreateArray();
    if (wl_null == ttsindexs_obj) {
        WLLOGW("create object fail");
        WLcJSON_Delete(store);
        return -1;
    }

    ttsindex = (repo_ttsindex*)wl_list_get_first(&s_repo_global.ttsindex_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        WLcJSON* ttsindex_obj = WLcJSON_CreateObject();
        if (wl_null != ttsindex_obj) {
            WLcJSON_AddNumberToObject(ttsindex_obj, "timestamp", ttsindex->timestamp);
            WLcJSON_AddNumberToObject(ttsindex_obj, "rshash", ttsindex->rshash);
            WLcJSON_AddNumberToObject(ttsindex_obj, "jshash", ttsindex->jshash);
            WLcJSON_AddNumberToObject(ttsindex_obj, "seq", ttsindex->seq);
            WLcJSON_AddItemToArray(ttsindexs_obj, ttsindex_obj);
        }
        ttsindex = (repo_ttsindex*)wl_list_get_next(&iterator);
    }
    if (!WLcJSON_AddItemToObject(store, "ttsindexs", ttsindexs_obj)) {
        WLLOGW("add item to obj fail");
        WLcJSON_Delete(store);
        WLcJSON_Delete(ttsindexs_obj);
        return -1;
    }
    repo_save(REPO_MSG_SAVE_STORE_TTSINDEX, store);
    WLcJSON_Delete(store);
    return 0;
}

void repo_set_tts_audio(wl_char* text, wl_uint32 textlen, wl_char* audio, wl_uint32 audiolen) {
    wl_uint16 seq      = 0;
    wl_bool   seqvalid = wl_false;
    wl_uint32 rshash   = RSHash(text, textlen);
    wl_uint32 jshash   = JSHash(text, textlen);

    // text已缓存,seq重用,同时删除ttsindex
    wl_list_iterator iterator;
    repo_ttsindex*   ttsindex = (repo_ttsindex*)wl_list_get_first(&s_repo_global.ttsindex_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (ttsindex->jshash == jshash && ttsindex->rshash == rshash) {
            seq      = ttsindex->seq;
            seqvalid = wl_true;
            wl_free(ttsindex);
            wl_list_iterator_remove(&iterator);
            break;
        }
        ttsindex = (repo_ttsindex*)wl_list_get_next(&iterator);
    }
    // text未缓存
    if (!seqvalid) {
        wl_char status[REPO_TTS_MAX];
        // 缓存已满,删除第一个
        if (wl_list_size(&s_repo_global.ttsindex_list) >= REPO_TTS_MAX) {
            ttsindex = (repo_ttsindex*)wl_list_get_first(&s_repo_global.ttsindex_list, &iterator);
            if (!wl_list_iterator_is_end(&iterator)) {
                wl_char path[MAX_NAME_LEN];
                sprintf(path, "tts_%d.ptt", ttsindex->seq);
                wl_ext_fs_delete(path);
                wl_free(ttsindex);
                wl_list_iterator_remove(&iterator);
            }
        }

        ttsindex = (repo_ttsindex*)wl_list_get_first(&s_repo_global.ttsindex_list, &iterator);
        while (!wl_list_iterator_is_end(&iterator)) {
            if (ttsindex->seq < REPO_TTS_MAX) {
                // seq占用
                status[ttsindex->seq] = 1;
            }
            ttsindex = (repo_ttsindex*)wl_list_get_next(&iterator);
        }
        for (seq = 0; seq < REPO_TTS_MAX; seq++) {
            if (0 == status[seq]) {
                seqvalid = wl_true;
                break;
            }
        }
    }

    if (seqvalid && seq < REPO_TTS_MAX) {
        wl_char path[MAX_NAME_LEN];
        sprintf(path, "tts_%d.ptt", seq);
        if (0 == repo_store_save(path, audio, audiolen)) {
            ttsindex = (repo_ttsindex*)wl_malloc(sizeof(repo_ttsindex));
            if (ttsindex) {
                ttsindex->seq       = seq;
                ttsindex->jshash    = jshash;
                ttsindex->rshash    = rshash;
                ttsindex->timestamp = repo_get_server_timestamp();
                // 新tts放末尾,列表满时直接删除第一个即可
                if (0 < wl_list_add(&s_repo_global.ttsindex_list, ttsindex, -1)) {
                    repo_ttsindex_save();
                }
            } else {
                WLLOGW("malloc ttsindex fail");
            }
        }
    }
}

wl_int32 repo_get_tts_audio(wl_char* text, wl_uint32 textlen, wl_char** audio, wl_uint32* audiolen) {
    wl_uint32 rshash = RSHash(text, textlen);
    wl_uint32 jshash = JSHash(text, textlen);

    // test已缓存,seq重用
    wl_list_iterator iterator;
    repo_ttsindex*   ttsindex = (repo_ttsindex*)wl_list_get_first(&s_repo_global.ttsindex_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        WLLOGD("ttsindex seq %d rshash %lu jshash %lu r %lu j %lu text %s",
               ttsindex->seq,
               ttsindex->rshash,
               ttsindex->jshash,
               rshash,
               jshash,
               text);
        if (ttsindex->jshash == jshash && ttsindex->rshash == rshash) {
            wl_char path[MAX_NAME_LEN];
            sprintf(path, "tts_%d.ptt", ttsindex->seq);
            return repo_store_read(path, audio, audiolen);
        }
        ttsindex = (repo_ttsindex*)wl_list_get_next(&iterator);
    }
    return -1;
}

#ifdef __cplusplus
}
#endif
